diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9b5f522b..4499c2c0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,6 +3,7 @@ # https://github.com/blog/2392-introducing-code-owners # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners -* @adilei @HenryJammes @nesrivastavaMS +* @adilei @HenryJammes -/CustomAnalytics @iMicknl @adilei @HenryJammes @nesrivastavaMS \ No newline at end of file +# ESS team owns their own content +/EmployeeSelfServiceAgent/ @microsoft/ess-contributors \ No newline at end of file diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..a5ace5de --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,175 @@ +# CopilotStudioSamples — Contributor Instructions + +This repo uses **Just the Docs** (Jekyll theme) to generate a documentation site with sidebar navigation and full-text search. + +## Adding a New Sample + +1. Create a folder under the right category: `authoring/`, `extensibility/`, `ui/`, `contact-center/`, `sso/`, `testing/`, `guides/`, `infrastructure/` +2. Add a `README.md` (exact casing) with YAML front matter: + +```yaml +--- +title: My Sample # Appears in sidebar nav +parent: Embed # Must match parent page's title exactly +grand_parent: UI # Required for level 3 (grandchild) pages +nav_order: 8 # Numeric position among siblings +--- +``` + +3. Write the README with: description, prerequisites, setup instructions +4. Update the parent category's `## Contents` table to include the new sample +5. Test locally: `bundle install && bundle exec jekyll serve` + +### Hierarchy Levels + +| Level | Front matter | Example | +|-------|-------------|---------| +| Category | `title`, `nav_order`, `has_children: true`, `has_toc: false` | `ui/README.md` | +| Subcategory | `title`, `parent`, `nav_order`, `has_children: true`, `has_toc: false` | `ui/embed/README.md` | +| Sample | `title`, `parent`, `grand_parent`, `nav_order` | `ui/embed/servicenow-widget/README.md` | +| Deep page | `nav_exclude: true`, `search_exclude: false` | Internal subfolders | + +### Power Platform Solutions (`authoring/solutions/`) + +Solutions follow the [PnP format](https://github.com/pnp/powerplatform-samples): + +``` +authoring/solutions/my-solution/ +├── README.md # Description, screenshots, install steps +├── assets/ # Screenshots and diagrams +├── solution/ # Packaged .zip file(s) ready to import +└── sourcecode/ # Unpacked source (pac solution unpack) +``` + +Front matter: +```yaml +--- +title: My Solution +parent: Solutions +grand_parent: Authoring +nav_order: 7 +--- +``` + +README should include: what it does, screenshots in `assets/`, import steps, connection references to configure, known issues. Update `authoring/solutions/README.md` Contents table after adding. + +### External Samples (code in another repo) + +Add `external_url` to front matter. This shows a "View sample in M365 Agents SDK repo" button instead of "Browse source on GitHub": + +```yaml +--- +title: Genesys Handoff +parent: Contact Center +nav_order: 4 +external_url: "https://github.com/microsoft/Agents/tree/main/samples/dotnet/GenesysHandoff" +--- +``` + +### Deprecated Samples + +Add a red label and caution callout: + +```markdown +Deprecated +{: .label .label-red } + +{: .caution } +> This sample is deprecated. Use [replacement](../path/) instead. +``` + +## Markdown Rules + +### Callouts — do NOT use GitHub `> [!NOTE]` syntax + +Jekyll doesn't support GitHub alerts. Use JTD kramdown callouts: + +```markdown +{: .note } +> This is a note. + +{: .warning } +> A warning. + +{: .tip } +> A tip. + +{: .caution } +> A caution. + +{: .important } +> Important info. +``` + +### Links Between Pages + +- README links: use directory path `[Sample](./my-sample/)` — NOT `(./my-sample/README.md)` +- Other .md files: drop the extension `[Setup](./SETUP)` — NOT `(./SETUP.md)` +- External URLs: use as-is + +### Labels + +```markdown +New +{: .label .label-green } + +Deprecated +{: .label .label-red } +``` + +### Liquid Escaping + +Markdown containing `{%` (e.g. URL-encoded params) must be wrapped: + +````markdown +{% raw %} +``` +https://example.com?q={%22key%22:%22value%22} +``` +{% endraw %} +```` + +## Building and Testing Locally + +```bash +# Install dependencies (first time only) +bundle install + +# Start dev server with live reload +bundle exec jekyll serve +# Site at http://127.0.0.1:4000/CopilotStudioSamples/ + +# Build without serving (CI check) +bundle exec jekyll build +``` + +Verify after changes: +- New page appears in sidebar nav +- Search finds the new sample (Ctrl+K) +- Internal links work (no 404s) +- Images render correctly + +## Git Workflow + +```bash +# Work on the docs branch +git checkout reorg/v1 + +# Make changes, then commit +git add -A +git commit -m "Add my-new-sample to ui/embed" + +# Push to fork — GitHub Actions deploys automatically +git push origin reorg/v1 +``` + +- **Branch**: `reorg/v1` is the docs branch. GitHub Actions builds and deploys to Pages on every push. +- **To merge upstream**: open a PR from `reorg/v1` → `main`. Update `.github/workflows/pages.yml` to trigger on `main` instead of `reorg/v1` before merging. +- **Remotes**: `origin` = fork (`microsoft/CopilotStudioSamples`), `upstream` = source (`microsoft/CopilotStudioSamples`) + +## Config Gotchas + +- `_config.yml` exclude patterns match recursively (`*` matches `/` in Jekyll) +- **Never add `*.html` to exclude** — it breaks theme layout files +- File must be named `README.md` (exact casing) for `jekyll-readme-index` to work +- Category READMEs use `has_toc: false` with a manual Contents table diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 00000000..ef163566 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,51 @@ +name: Deploy Jekyll site to Pages + +on: + push: + branches: ["main"] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.3" + bundler-cache: true + + - name: Setup Pages + id: pages + uses: actions/configure-pages@v5 + + - name: Build with Jekyll + run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}" + env: + JEKYLL_ENV: production + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 27059e02..330519b8 100644 --- a/.gitignore +++ b/.gitignore @@ -262,6 +262,7 @@ FakesAssemblies/ # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ +package-lock.json # Visual Studio 6 build log *.plg @@ -331,3 +332,8 @@ ASALocalRun/ /sabacha/Dispatcher/dispatcher.bot SharePointSSOComponent/package-lock.json SharePointSSOComponent/config/serve.json + +# Jekyll +_site/ +.jekyll-cache/ +.jekyll-metadata diff --git a/3rdPartySSOWithOKTA/README.md b/3rdPartySSOWithOKTA/README.md deleted file mode 100644 index 8f1dc116..00000000 --- a/3rdPartySSOWithOKTA/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# 3rd Party SSO with OKTA - -This custom canvas demonstrates how an access token obtained from a 3rd party identity provider, like OKTA, can be used in the context of a single sign-on (SSO) login flow with Copilot Studio. - -## Getting started - -To run this sample, including the end-to-end SSO flow with OKTA, you will need to: - -1. Deploy [index.html](./public/index.html) and [signout.html](./public/signout.html) on a remote or local server -2. Create an OKTA developer account, or use an existing one -3. Create a new app integration in OKTA -4. Configure the default access policy in the OTKA authorization server -5. Retrieve the token endpoint for a custom copilot that is configured with manual authentication -6. Update configuration values in index.html - -## Detailed instructions - -### Deploy the sample files - -Deploy [index.html](./public/index.html) and [signout.html](./public/signout.html) on a local or a remote server, so they are available via two URLs. For example: [http://localhost:8080/index.html](http://localhost:8080/index.html) and [http://localhost:8080/signout.html](http://localhost:8080/signout.html) - -### Configure OKTA - -1. Sign up for an [OKTA developer account](https://developer.okta.com/signup/) -2. Sign in to the OKTA admin dashboard at **https://{your domain}-admin.okta.com/** and create a new app integration with the following details. - - -| Application Property | Value | -| ---------------------- | ------------------------------------------------------------------- | -| Sign-in method | OIDC - OpenID Connect | -| Application type | Single-Page Application | -| Grant type | Authorization Code, Interaction Code | -| Sign-in redirect URIs | the URL to index.html | -| Sign-out redirect URIs | the URL to signout.html | -| Trusted origins | your base URL, for example http://localhost:8080 | -| Assignments | allow access to specific users or groups based on your requirements | - -3. After creating the app integration, note its Client ID -4. **Index.html** uses the OKTA sign-in widget which relies on the Interaction Code sign-in flow. To enable the Interaction Code flow: - - 1. Navigate to the API settings page under ***Security -> API*** - 2. Under the Authorization Servers tab, edit the default authorization server - 3. Under Access Policies, edit the default policy rule - 4. Under ***IF Grant type is*** -> ***Other grants***, click on **Interaction Code**. - 5. Update the rule - 6. You should also verify that CORS has been enabled for your base URL. On the same API page, under the ***Trusted Origins*** tab, your base url (e.g. http://localhost:8080) should appear under ***Trusted Origins*** with CORS enabled. In case your base url is missing, add the url with CORS enabled. - - -### Configure authentication in Copilot Studio, and obtain the token endpoint - -1. This SSO pattern will work for copilots configured with [manual authentication and any OAuth authentication provider](https://learn.microsoft.com/en-us/microsoft-copilot-studio/configuration-end-user-authentication#manual-authentication-fields). Since it is a passthrough pattern, in which the token is sent to Copilot Studio, but not validated, it will even work when no values are provided for an authentication provider. To configure manual authentication without providing any real values, select "Generic OAuth 2.0" and enter **placeholder** in required fields. - -
-
-
- Manual authentication without real values
-
-
-
- The OKTA sign-in widget
-
-
-
- System.User.AccessToken is populated
-
-
-## Solution components
-
-- Microsoft Dataverse
-- Microsoft Power BI DataFlows
-- Microsoft Power BI Desktop
-- Chat Transcripts control for Power BI - available at
-
-## Solution components
-
-- Microsoft Dataverse
-- Microsoft Power BI Desktop
-- [Chat Transcripts visual for Power BI](https://github.com/iMicknl/powerbi-botframework-chat-transcripts) - many thanks to Mick Vleeshouwer
-
-## Solution files
-
-- [PVA_Dashboard.pbit](https://github.com/microsoft/CopilotStudioSamples/raw/master/CustomAnalytics/PVA_Dashboard.pbit) - Power BI template file
-
-## Installation
-
-### Installation requirements
-
-- One or more copilots created in Microsoft Copilot Studio
-- A Power BI account
-- [Power BI Desktop](https://powerbi.microsoft.com/en-us/downloads/)
-- [TDS endpoint in Dataverse enabled](https://learn.microsoft.com/en-us/power-query/connectors/dataverse#prerequisites)
-
-### Installation steps
-
-1. Create your PowerBI report
- 1. Download and open the file [PVA_Dashboard.pbit](https://github.com/microsoft/CopilotStudioSamples/raw/master/CustomAnalytics/PVA_Dashboard.pbit)
- 2. Enter the parameters you are prompted for.
- 1. The URI of you Dataverse instance (e.g. mydataverse.crm.dynamics.com - note the removal of 'https://' and the trailing slash). _[Finding your Dataverse environment URL](https://learn.microsoft.com/en-us/power-query/connectors/dataverse#finding-your-dataverse-environment-url)_.
- 3. The report should pull through the data and render it. If you see an error, please check the troubleshooting section below.
- 4. You can now publish the report to Power BI Service, and share it with your users.
-
-## Using the report
-
-The report is based on what is shared on the analytics pane in Microsoft Copilot Studio, but with some important differences:
-
-- The report includes all bots in your environment
-- The report includes a date filter - and can support a range of dates greater than the 30 days supported natively in Dataverse
-- The report includes all trace data emitted with the bot, allowing users to build reports on data import to them
-- The report includes all transcripts, with a transcript viewer, to learn from your customer conversations
-- The report can be shared with users and business decision makers without access to Power Virtual Agents
-
-## Troubleshooting
-
-1. Ensure the TDS endpoint in Dataverse is enabled and you have permission to access data within tables. Empty data tables may indicate a permissions problem. See [Dataverse Prerequisites](https://learn.microsoft.com/en-us/power-query/connectors/dataverse#prerequisites).
-1. When using the Dataverse connector, if you experience an "Unable to connect" error due to a network or instance-specific issue, it may be because TCP ports 1433 or 5558 are blocked, see [SQL Server connection issue due to closed ports](https://learn.microsoft.com/en-us/power-query/connectors/dataverse#sql-server-connection-issue-due-to-closed-ports).
-1. Please raise [a new issue](https://github.com/microsoft/CopilotStudioSamples/issues/new/choose) in this repository for other problems.
diff --git a/EmployeeSelfServiceAgent/ESSEvaluationSamples/README.md b/EmployeeSelfServiceAgent/ESSEvaluationSamples/README.md
new file mode 100644
index 00000000..075c4f7a
--- /dev/null
+++ b/EmployeeSelfServiceAgent/ESSEvaluationSamples/README.md
@@ -0,0 +1,114 @@
+---
+title: Evaluation Samples
+parent: Employee Self-Service
+nav_order: 3
+---
+# ESS Agent Evaluation Resources
+
+This repository provides guidance and ready-to-use resources for evaluating the **Microsoft Employee Self‑Service (ESS)** agent built in **Copilot Studio**. It is designed to help teams create consistent, repeatable, and scalable evaluation workflows across HR and IT scenarios.
+
+---
+
+## 📘 Overview
+
+Use this repository to:
+
+- Understand how to create test sets for ESS.
+- Run automated evaluations using Copilot Studio.
+- Interpret evaluation results (accuracy, safety, grounding, completeness, etc.).
+- Build a repeatable quality strategy for ESS deployments.
+- Develop custom golden query sets for your organization.
+
+The documentation covers:
+
+- **Knowledge tests**
+- **Data and topic tests**
+- **Conversational quality checks**
+- **Recommended practices** for high‑quality ESS evaluation cycles
+
+---
+
+## 📂 Repository Structure
+
+### 1. StarterTestSets (Ready to Use)
+
+Fully prepared test sets you can upload directly into Copilot Studio's Evaluation tool.
+
+- No changes needed.
+- Helpful for quick validation, demos, and baseline evaluations.
+
+### 2. TemplatedTestSets (Partially Ready – Requires Input)
+
+These test sets include predefined templates with placeholder values that **you must fill** before use.
+
+Example placeholder:
+
+```
+What is my employee ID? Employee ID <21514>
+```
+Screenshot:
+
+, Compa‑ratio <0.00>",CompareMeaning,70
+What is my Cost Center?,Cost center <1234-5678 + Name>,CompareMeaning,70
+What is my employee ID?,Employee ID <123456>,CompareMeaning,70
+Show me my job details,"Job title , job classification , job function , job function type ",CompareMeaning,70
+What is my position ID?,Position ID <12345678>,CompareMeaning,70
+Show my service anniversary date,Service anniversary date and milestone ,CompareMeaning,70
+What is my company code?,Company code <1234 + Company Name>,CompareMeaning,70
+How much can I expect to earn annually?,Annual base salary ,CompareMeaning,70
+I need to correct my email in HR.,Email address on file ,CompareMeaning,70
+How do I update my emergency contact?,"Emergency contacts on file ",CompareMeaning,70
+I need to add a new work phone,Phone numbers on file ,CompareMeaning,70
+I want to update my preferred name.,Preferred name on file ,CompareMeaning,70
+Do you have my national ID on file?,"National ID details ",CompareMeaning,70
+Show me the company code for my direct reports,Company code for direct reports ,CompareMeaning,70
+Show me the cost center for my team members,Cost center for team members ,CompareMeaning,70
+Show me the job information for ,"Job information ",CompareMeaning,70
+What is my team member’s work anniversary?,"Team member service anniversaries ",CompareMeaning,70
+What is the work anniversary for ?,"Service anniversary ",CompareMeaning,70
+I need to update my employee’s job title.,Current job titles for employees ,CompareMeaning,70
+Help me update the job title for ,Job title update workflow ,CompareMeaning,70
+What is the hire date for ?,Hire date ,CompareMeaning,70
diff --git a/EmployeeSelfServiceAgent/ESSEvaluationSamples/TemplatedTestSets/Workday.csv b/EmployeeSelfServiceAgent/ESSEvaluationSamples/TemplatedTestSets/Workday.csv
new file mode 100644
index 00000000..f5fbee9b
--- /dev/null
+++ b/EmployeeSelfServiceAgent/ESSEvaluationSamples/TemplatedTestSets/Workday.csv
@@ -0,0 +1,22 @@
+Prompt,Expected response ,Test Method Type,Passing Score
+What is my employee ID?,Employee ID <21514>,CompareMeaning,70
+What is my cost center?,Cost center <12345>,CompareMeaning,70
+What's my base compensation?,Base compensation ,CompareMeaning,70
+What languages are listed in my employee profile?,"Languages on file ",CompareMeaning,70
+What employment information is on file for me?,"Employment type , FTE <100%>, hire dates , location , management level , position ID ",CompareMeaning,70
+Can you show me the passport I have on file?,Passport information ,CompareMeaning,70
+Show me the government ID I have on file,Government ID information ,CompareMeaning,70
+What is my service anniversary,Service anniversary date and milestone ,CompareMeaning,70
+Show me the Visa information I have in my employee profile,Visa information ,CompareMeaning,70
+Show me my employee profile summary.,"Employee profile summary ",CompareMeaning,70
+Who is listed as my emergency contact?,"Emergency contacts ",CompareMeaning,70
+How can I update my preferences for being contacted by phone?,Phone contact preference settings ,CompareMeaning,70
+Show me my certifications on file.,Certifications on file ,CompareMeaning,70
+Check whether my job history is available in my profile.,Job history availability ,CompareMeaning,70
+What is the process for updating my dependent's information?,Dependents on file ,CompareMeaning,70
+What is my official job title?,Job title ,CompareMeaning,70
+Do I have access to view my compensation data?,Base compensation ,CompareMeaning,70
+Are there any trainings and certifications needed for my role?,Required trainings or certifications ,CompareMeaning,70
+Where can I see my official work location?,Work location ,CompareMeaning,70
+What is my language preference and how can I update it for HR communication?,Preferred language information ,CompareMeaning,70
+Show me my hire date and my rehire date if I have one.,Hire date and rehire date ,CompareMeaning,70
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeCreateFacilitiesManagementTicket/Extract Location Information.md b/EmployeeSelfServiceAgent/Facilities/EmployeeCreateFacilitiesManagementTicket/Extract Location Information.md
new file mode 100644
index 00000000..d319f652
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeCreateFacilitiesManagementTicket/Extract Location Information.md
@@ -0,0 +1,23 @@
+You are tasked with extracting location information from a user-provided text that describes issues related to their facilities.
+
+### Instructions:
+1. Analyze the input text carefully to identify any mention of locations. These may include:
+ - Addresses
+ - City, state, or country names
+ - Landmarks or well-known places
+ - Specific facility names or site identifiers
+ - Specific room names that are commonly used
+2. Extract all relevant location details explicitly mentioned in the text.
+3. If multiple locations are mentioned, list each separately.
+4. Avoid inferring locations that are not clearly stated in the text.
+5. Present the extracted location information in a clear and structured manner.
+
+### Output Format:
+- Provide only the extracted locations as a string.
+- If no location information is found, respond with an empty string.
+
+Example:
+Input Text: "The air conditioning in our New York office is malfunctioning, and the warehouse in Brooklyn also has issues."
+Output: New York office, warehouse in Brooklyn
+
+Provide the input text describing the facilities problem here:
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeCreateFacilitiesManagementTicket/Extract Problem category.md b/EmployeeSelfServiceAgent/Facilities/EmployeeCreateFacilitiesManagementTicket/Extract Problem category.md
new file mode 100644
index 00000000..4b2fb4b3
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeCreateFacilitiesManagementTicket/Extract Problem category.md
@@ -0,0 +1,19 @@
+You are tasked with extracting the industry-standard category that best describes a facilities management problem reported by an employee. Use the provided problem description and match it accurately to the relevant category from the supplied reference data.
+
+### Instructions:
+1. **Analyze the Problem Description:** Carefully read the employee’s detailed description of the facilities management issue.
+2. **Reference Data Matching:** Compare the problem details against the provided industry-standard categories data to find the most appropriate category that describes the problem.
+3. **Grounding:** Ensure the selected category is directly supported by the input reference data without assumptions or external knowledge.
+4. **Output:** Return the matched category as a concise text string.
+
+### Guidelines:
+- Extract only from the provided problem description and reference data.
+- Avoid guessing or inferring categories not present in the reference data.
+- If no suitable category is found, indicate this clearly.
+
+### Output Format:
+Provide the matched industry-standard category as plain text. If no match is found, respond with: "No matching category found."
+
+Provide the following inputs:
+- Problem Description: Problem Description
+- Reference Categories Data: Categories
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeCreateFacilitiesManagementTicket/msdyn_msdyn_HRWorkdayHCMEmployeeGetVacationBalance.xml b/EmployeeSelfServiceAgent/Facilities/EmployeeCreateFacilitiesManagementTicket/msdyn_msdyn_HRWorkdayHCMEmployeeGetVacationBalance.xml
new file mode 100644
index 00000000..b9a4f571
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeCreateFacilitiesManagementTicket/msdyn_msdyn_HRWorkdayHCMEmployeeGetVacationBalance.xml
@@ -0,0 +1,10 @@
+
+ 9
+ 0
+ Create Facilities Management Ticket
+
+ msdyn_copilotforemployeeselfservice
+
+ 0
+ 1
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeCreateFacilitiesManagementTicket/topic.yaml b/EmployeeSelfServiceAgent/Facilities/EmployeeCreateFacilitiesManagementTicket/topic.yaml
new file mode 100644
index 00000000..23a4abfa
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeCreateFacilitiesManagementTicket/topic.yaml
@@ -0,0 +1,177 @@
+kind: AdaptiveDialog
+modelDescription: "This tool can handle queries like these: create facilities request, open a facilities ticket, start a new facilities request."
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - create facilities request
+ - open a facilities ticket
+ - start a new facilities request
+ - file a facilities complaint
+ - Help me with a workspace problem
+ - Something is wrong in the building
+ - Create a ticket for facilities
+ - Request maintenance for office
+ - Report a broken chair/light/door
+ - I need to submit a facilities request
+
+ actions:
+ - kind: SetVariable
+ id: setVariable_Z2HdOA
+ variable: Topic.ProblemDescription
+ value: =System.Activity.Text
+
+ - kind: SendActivity
+ id: sendActivity_uEi72P
+ activity: "Thanks for the details you've shared so far. I’ll review the information to determine if everything needed to submit your facilities request is already available. "
+
+ - kind: InvokeAIBuilderModelAction
+ id: invokeAIBuilderModelAction_inqoh4
+ displayName: Prompt Location
+ input:
+ binding:
+ Facilities_20Description: =Topic.ProblemDescription
+
+ output:
+ binding:
+ predictionOutput: Topic.ProblemLocation
+
+ aIModelId: 0bde30fa-b6bd-42f3-a9b6-d58c47ca2065
+
+ - kind: InvokeAIBuilderModelAction
+ id: invokeAIBuilderModelAction_3wGDBR
+ displayName: Prompt Category
+ input:
+ binding:
+ Categories: ="HVAC, Electrical, Plumbing, Maintenance"
+ Problem_20Description: =Topic.ProblemDescription
+
+ output:
+ binding:
+ predictionOutput: Topic.ProblemCategory
+
+ aIModelId: 49e40e64-a630-4692-a82b-4f88522eba41
+
+ - kind: AdaptiveCardPrompt
+ id: "sendActivity_CtL3qw "
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Details of your Service Request",
+ wrap: true,
+ style: "heading"
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "ProblemCategory",
+ label: "Problem Category:",
+ value: Topic.ProblemCategory.text,
+ weight: "bolder",
+ isRequired: true,
+ errorMessage: "Fill required details"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "ProblemLocation",
+ label: "Problem Location:",
+ value: Topic.ProblemLocation.text,
+ weight: "bolder",
+ isRequired: true,
+ errorMessage: "Fill required details"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "ProblemDescription",
+ label: "Problem Description:",
+ value: Topic.ProblemDescription,
+ weight: "bolder",
+ isRequired: true,
+ errorMessage: "Fill required details"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ actions: [
+ {
+ type: "Action.Submit",
+ title: "Submit"
+ },
+ {
+ type: "Action.Submit",
+ title: "Cancel",
+ associatedInputs: "none"
+ }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.actionSubmitId
+
+ outputType:
+ properties:
+ actionSubmitId: String
+
+ - kind: ConditionGroup
+ id: conditionGroup_guZXij
+ conditions:
+ - id: conditionItem_NnWEYN
+ condition: =Topic.actionSubmitId = "Submit"
+ actions:
+ - kind: InvokeFlowAction
+ id: invokeFlowAction_qSOIaY
+ input:
+ binding:
+ text: =Topic.ProblemCategory.text
+ text_1: =Topic.ProblemLocation.text
+ text_2: =Topic.ProblemDescription
+
+ output:
+ binding:
+ problemticket: Topic.ProblemTicket
+
+ flowId: 521ce2a6-daaa-f011-bbd2-0022480b25f5
+
+ elseActions:
+ - kind: SendActivity
+ id: sendActivity_BaWHYn
+ activity: Let us know how can we help.
+
+inputType: {}
+outputType: {}
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_DiningGetMenusQuery..xml b/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_DiningGetMenusQuery..xml
new file mode 100644
index 00000000..d4d7471f
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_DiningGetMenusQuery..xml
@@ -0,0 +1,15 @@
+
+ 9
+ This topic helps users find and view menus available at Microsoft cafes. The cafes are structured hierarchically as follows:
+
+- Building → Cafe → Station → Menus → Menu Items
+
+Each building contains one or more cafes. Each cafe contains multiple food stations, each offering distinct food items and menus.
+ 0
+ Fac Dining Get Menus Query
+
+ msdyn_copilotforemployeeselfservice
+
+ 0
+ 1
+
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningDisplayMenus.yaml b/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningDisplayMenus.yaml
new file mode 100644
index 00000000..e2c92264
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningDisplayMenus.yaml
@@ -0,0 +1,130 @@
+kind: AdaptiveDialog
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: CafeName
+ description: (Required) Confirmed cafe name or identifier
+ entity: StringPrebuiltEntity
+ shouldPromptUser: true
+
+ - kind: AutomaticTaskInput
+ propertyName: StationName
+ description: (Required) Confirmed station name or identifier
+ entity: StringPrebuiltEntity
+ shouldPromptUser: true
+
+ - kind: AutomaticTaskInput
+ propertyName: CafeId
+ description: (Optional) The ID of the cafe to retrieve menus for (more reliable)
+ entity: StringPrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+modelDescription: This topic retrieves the menus available at a specified cafe and station.
+beginDialog:
+ kind: OnRedirect
+ id: main
+ actions:
+ - kind: SetVariable
+ id: setVariable_ApiUrl
+ variable: Topic.ApiUrl
+ value: |-
+ =Concatenate(
+ Env.fac_DiningServiceApiUrl,
+ "/api/menus?cafeName=",
+ EncodeUrl(Topic.CafeName),
+ "&stationName=",
+ EncodeUrl(Topic.StationName)
+ )
+
+ - kind: HttpRequestAction
+ id: bBZmwA
+ url: =Topic.ApiUrl
+ headers:
+ clientSource: =System.Activity.ChannelId
+ Content-Type: application/json
+ x-ms-obo-scope: =Env.fac_DiningServiceApiOboScope
+
+ errorHandling:
+ kind: ContinueOnErrorBehavior
+ statusCode: Topic.CafeStationMenusStatusCode
+
+ requestTimeoutInMilliseconds: 90000
+ response: Topic.CafeStationMenus
+ responseSchema:
+ kind: Record
+ properties:
+ count: Number
+ items:
+ type:
+ kind: Table
+ properties:
+ cafeName: String
+ isAvailableOnline: Boolean
+ items:
+ type:
+ kind: Table
+ properties:
+ name: String
+
+ name: String
+ stationName: String
+
+ - kind: ConditionGroup
+ id: conditionGroup_CheckResults
+ conditions:
+ - id: conditionItem_HasMenus
+ condition: =!IsBlank(Topic.CafeStationMenus) && CountRows(Topic.CafeStationMenus.items) > 0
+
+ elseActions:
+ - kind: SetVariable
+ id: setVariable_EmptyMenus
+ variable: Topic.CafeStationMenus
+ value: |-
+ ={
+ count: 0,
+ items: []
+ }
+
+inputType:
+ properties:
+ CafeId:
+ displayName: CafeId
+ description: (Optional) The ID of the cafe to retrieve menus for (more reliable)
+ type: String
+
+ CafeName:
+ displayName: CafeName
+ description: (Required) Confirmed cafe name or identifier
+ type: String
+
+ StationName:
+ displayName: StationName
+ description: (Required) Confirmed station name or identifier
+ type: String
+
+outputType:
+ properties:
+ CafeStationMenus:
+ displayName: CafeStationMenus
+ type:
+ kind: Record
+ properties:
+ count: Number
+ items:
+ type:
+ kind: Table
+ properties:
+ cafeId: String
+ cafeName: String
+ isAvailableOnline: Boolean
+ items:
+ type:
+ kind: Table
+ properties:
+ name: String
+
+ name: String
+ stationId: String
+ stationName: String
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningGetCafeStations.yaml b/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningGetCafeStations.yaml
new file mode 100644
index 00000000..87a0ea9a
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningGetCafeStations.yaml
@@ -0,0 +1,210 @@
+kind: AdaptiveDialog
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: CafeName
+ description: The name of the cafe to retrieve stations for
+ entity: StringPrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+ - kind: AutomaticTaskInput
+ propertyName: CafeId
+ description: The ID of the cafe to retrieve stations for (more reliable than name)
+ entity: StringPrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+modelDescription: |-
+ This topic fetches stations for the provided cafe and asks the user to select one.
+
+ The user can say "Show me stations for 31" or "Show me the stations in 31" etc. In these examples, 31 is the cafeName.
+beginDialog:
+ kind: OnRedirect
+ id: main
+ actions:
+ - kind: SetVariable
+ id: setVariable_CurrentDayOfWeek
+ variable: Topic.CurrentDayOfWeek
+ value: =Mod(Weekday(Now(), StartOfWeek.Monday), 7)
+
+ - kind: SetVariable
+ id: setVariable_ApiUrl
+ variable: Topic.ApiUrl
+ value: |-
+ =Concatenate(
+ Env.fac_DiningServiceApiUrl,
+ "/api/menus?cafeName=",
+ EncodeUrl(Topic.CafeName),
+ "&dayOfWeek=",
+ Text(Topic.CurrentDayOfWeek)
+ )
+
+ - kind: HttpRequestAction
+ id: httpRequest_GetStations
+ url: =Topic.ApiUrl
+ headers:
+ clientSource: =System.Activity.ChannelId
+ Content-Type: application/json
+ x-ms-obo-scope: =Env.fac_DiningServiceApiOboScope
+
+ errorHandling:
+ kind: ContinueOnErrorBehavior
+ statusCode: Topic.CafeStationsStatusCode
+
+ requestTimeoutInMilliseconds: 90000
+ response: Topic.CafeStationsResponse
+ responseSchema:
+ kind: Record
+ properties:
+ count: Number
+ items:
+ type:
+ kind: Table
+ properties:
+ cafeId: String
+ cafeName: String
+ description: String
+ id: String
+ isAvailableOnline: Boolean
+ name: String
+ stationId: String
+ stationName: String
+
+ metadata: Blank
+
+ - kind: ConditionGroup
+ id: conditionGroup_ApiError
+ conditions:
+ - id: conditionItem_ApiSuccess
+ condition: =!IsBlank(Topic.CafeStationsResponse) && Topic.CafeStationsResponse.count > 0
+ actions:
+ - kind: SetVariable
+ id: setVariable_0jCNqg
+ variable: Topic.CafeStations
+ value: |-
+ =ForAll(
+ Topic.CafeStationsResponse.items,
+ {
+ stationId: stationId,
+ cafeId: cafeId,
+ DisplayText: Text(stationName),
+ Value: Text(stationName)
+ }
+ )
+
+ - kind: SetVariable
+ id: setVariable_StationNames
+ variable: Topic.StationNames
+ value: |-
+ =Distinct(ForAll(
+ Topic.CafeStationsResponse.items,
+ Text(stationName)
+ ), Value)
+
+ - kind: ConditionGroup
+ id: conditionGroup_CheckStations
+ conditions:
+ - id: conditionItem_HasStations
+ condition: =!IsEmpty(Topic.StationNames)
+ actions:
+ - kind: ConditionGroup
+ id: conditionGroup_qPZhwJ
+ conditions:
+ - id: conditionItem_Q2tZoT
+ condition: =CountRows(Topic.StationNames) = 1
+ displayName: If the length of Station Names is equal to one
+ actions:
+ - kind: SetVariable
+ id: aBTUCL
+ variable: Topic.StationId
+ value: =LookUp(Topic.CafeStations,Value= First(Topic.StationNames).Value,stationId)
+
+ elseActions:
+ - kind: Question
+ id: iHiPXO
+ interruptionPolicy:
+ allowInterruption: true
+
+ variable: Topic.StationName
+ prompt: |-
+ Which station in {Topic.CafeName} do you want to see the menus for?
+ {Char (10) & Concat(
+ Topic.StationNames,
+ "- " & Text(Value) & Char(10)
+ )}
+
+ Please reply with the **full name of the station** from the list above to see its menu.
+ entity:
+ kind: DynamicClosedListEntity
+ items: =Topic.StationNames
+ samples:
+
+ - kind: SetVariable
+ id: d2Zb64
+ variable: Topic.StationId
+ value: =LookUp(Topic.CafeStations,Value=Topic.StationName,stationId)
+
+ elseActions:
+ - kind: SendActivity
+ id: sendActivity_NoStations
+ activity: Sorry, we couldn't find any stations for {Topic.CafeName}
+
+ - kind: SetVariable
+ id: setVariable_NoStationSelected
+ variable: Topic.StationName
+ value: =""
+
+ - kind: SetVariable
+ id: setVariable_EKAEn4
+ variable: Topic.StationId
+ value: =Blank()
+
+ - id: conditionItem_bL1rlz
+ condition: =Topic.CafeStationsStatusCode > 200
+ actions:
+ - kind: SendActivity
+ id: sendActivity_9m6mku
+ activity: Sorry, I couldn't retrieve station information at the moment. Please try again later.
+
+ - kind: SetVariable
+ id: setVariable_GJtzjn
+ variable: Topic.StationName
+ value: =""
+
+ elseActions:
+ - kind: SendActivity
+ id: sendActivity_ApiError
+ activity: There are currently no menus available to show
+
+ - kind: SetVariable
+ id: setVariable_ErrorStation
+ variable: Topic.StationName
+ value: =""
+
+inputType:
+ properties:
+ CafeId:
+ displayName: CafeId
+ description: The ID of the cafe (more reliable than name)
+ type: String
+
+ CafeName:
+ displayName: CafeName
+ description: The name of the cafe to retrieve stations for
+ type: String
+
+outputType:
+ properties:
+ StationId:
+ displayName: StationId
+ description: The ID of the selected station
+ type: String
+
+ StationName:
+ displayName: StationName
+ description: Name of the selected station
+ type: String
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningGetCafesList.yaml b/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningGetCafesList.yaml
new file mode 100644
index 00000000..b51f9853
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningGetCafesList.yaml
@@ -0,0 +1,165 @@
+kind: AdaptiveDialog
+beginDialog:
+ kind: OnRedirect
+ id: main
+ actions:
+ - kind: SetVariable
+ id: setVariable_rDQ3EZ
+ variable: Topic.CountryCode
+ value: =Global.ESS_UserContext_Country_Code
+
+ - kind: SetVariable
+ id: setVariable_wNGkRp
+ variable: Topic.IsCountryCodeValid
+ value: =And(!IsBlank(Topic.CountryCode), !IsMatch(Topic.CountryCode, ""), !IsMatch(Topic.CountryCode, "''"))
+
+ - kind: HttpRequestAction
+ id: PqB360
+ url: |-
+ =Concatenate(Env.fac_DiningServiceApiUrl, "/api/v2/cafes/list?officeLocationRadius=50",
+ If(Topic.IsCountryCodeValid, "&countryCode=" & Topic.CountryCode))
+ headers:
+ clientSource: =System.Activity.ChannelId
+ Content-Type: application/json
+ x-ms-obo-scope: =Env.fac_DiningServiceApiOboScope
+
+ errorHandling:
+ kind: ContinueOnErrorBehavior
+ statusCode: Topic.CafesListResponseStatusCode
+
+ requestTimeoutInMilliseconds: 90000
+ response: Topic.CafesListResponse
+ responseSchema:
+ kind: Record
+ properties:
+ count: Number
+ items:
+ type:
+ kind: Table
+ properties:
+ buildingId: String
+ buildingName: String
+ cafeId: Number
+ cityName: Blank
+ coordinates:
+ type:
+ kind: Record
+ properties:
+ latitude: Number
+ longitude: Number
+
+ countryCode: Blank
+ currencyName: String
+ currencySymbol: String
+ disclaimer: Blank
+ id: String
+ imageUrl: String
+ legacyBuildingId: Number
+ location:
+ type:
+ kind: Record
+ properties:
+ coordinates:
+ type:
+ kind: Table
+ properties:
+ Value: Number
+
+ latitude: Number
+ longitude: Number
+ type: String
+
+ mealPeriods:
+ type:
+ kind: Table
+ properties:
+ customPeriod: Blank
+ endsOn: String
+ hoursOfOperation: String
+ id: String
+ name: String
+ startsOn: String
+ timeStamp: String
+
+ name: String
+ onlineAvailabilityCapability: String
+ percentOrdersForSixtyDays: Number
+ regionId: String
+ serviceHours: String
+ stateOrProvinceCode: Blank
+ stateOrProvinceName: Blank
+ timestamp: String
+
+ metadata: Blank
+
+ - kind: ConditionGroup
+ id: conditionGroup_R9eHgz
+ conditions:
+ - id: conditionItem_nAOxBE
+ condition: =!IsBlank(Topic.CafesListResponse.items) And CountRows(Topic.CafesListResponse.items) > 0
+ actions:
+ - kind: SetVariable
+ id: setVariable_Qx1AzM
+ variable: Topic.CafesCount
+ value: =Topic.CafesListResponse.count
+
+ - kind: SetVariable
+ id: setVariable_YpU0zY
+ variable: Topic.CafesList
+ value: |-
+ =ForAll(
+ Topic.CafesListResponse.items,
+ {
+ id: ThisRecord.id,
+ name: ThisRecord.name,
+ buildingId: ThisRecord.buildingId,
+ buildingName: ThisRecord.buildingName,
+ cafeId: ThisRecord.cafeId,
+ onlineAvailabilityCapability: ThisRecord.onlineAvailabilityCapability
+ }
+ )
+
+ elseActions:
+ - kind: SetVariable
+ id: setVariable_gW0pM5
+ variable: Topic.CafesCount
+ value: 0
+
+ - kind: ParseValue
+ id: wrGfId
+ variable: Topic.CafesList
+ valueType:
+ kind: Table
+ properties:
+ count: Number
+ items:
+ type:
+ kind: Table
+
+ value: "{ count: 0, items: [] }"
+
+inputType: {}
+outputType:
+ properties:
+ CafesCount:
+ displayName: CafesCount
+ description: Number of cafes returned
+ type: Number
+
+ CafesList:
+ displayName: CafesList
+ description: List of cafes with essential fields only
+ type:
+ kind: Table
+ properties:
+ buildingId: String
+ buildingName: String
+ cafeId: Number
+ id: String
+ name: String
+ onlineAvailabilityCapability: String
+
+ CafesListResponseStatusCode:
+ displayName: CafesListResponseStatusCode
+ description: HTTP status code from the API call (blank if successful)
+ type: Number
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningHandleMultipleCafeMatches_JAK.yaml b/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningHandleMultipleCafeMatches_JAK.yaml
new file mode 100644
index 00000000..de6c6e6d
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningHandleMultipleCafeMatches_JAK.yaml
@@ -0,0 +1,215 @@
+kind: AdaptiveDialog
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: MatchedCafes
+ description: Cafes that matched user's search term
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+ - kind: AutomaticTaskInput
+ propertyName: SearchQuery
+ description: User's search query
+ entity: StringPrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+modelDisplayName: Fac Dining Handle Multiple Cafe Matches
+modelDescription: This topic handles multiple matches for the provided cafe name
+beginDialog:
+ kind: OnRedirect
+ id: main
+ actions:
+ - kind: SetVariable
+ id: setVariable_0gVXLI
+ variable: Topic.MatchCount
+ value: =CountRows(Topic.MatchedCafes)
+
+ - kind: SetVariable
+ id: setVariable_ibw6Fx
+ variable: Topic.CafeSelectionOptions
+ value: |-
+ =Filter(
+ ForAll(Topic.MatchedCafes,
+ {
+ name: name,
+ cafeId: id,
+ buildingName: buildingName,
+ DisplayName: Concatenate(
+ name,
+ " (Building ", buildingName,")")
+ }
+ ),
+ !IsBlank(name)
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_BAeZlG
+ conditions:
+ - id: conditionItem_fJDo4P
+ condition: =Topic.MatchCount >= 20
+ actions:
+ - kind: AdaptiveCardPrompt
+ id: zcc1CC
+ interruptionPolicy:
+ allowInterruption: false
+
+ repeatCount: 0
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "https://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "Container",
+ items: [
+ {
+ type: "TextBlock",
+ text: "Select a Cafe",
+ size: "Medium",
+ weight: "Bolder",
+ wrap: true
+ },
+ {
+ type: "TextBlock",
+ text: "Choose from the cafes below:",
+ wrap: true,
+ spacing: "Small",
+ isSubtle: true
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "SelectedCafeId",
+ errorMessage: "Please select a cafe",
+ spacing: "Medium",
+ label: "Available Cafes",
+ placeholder: "Select a cafe...",
+ isRequired: true,
+ style: "compact",
+ choices: ForAll(Topic.CafeSelectionOptions, {
+ title: DisplayName,
+ value: cafeId
+ })
+ },
+ {
+ type: "ActionSet",
+ actions: [
+ {
+ type: "Action.Submit",
+ title: "Submit",
+ style: "positive",
+ tooltip: "Submit cafe selection"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ output:
+ binding:
+ SelectedCafeId: Topic.SelectedCafeId
+
+ outputType:
+ properties:
+ SelectedCafeId: String
+
+ - kind: SetVariable
+ id: setVariable_fWNxAM
+ variable: Topic.SelectedCafe
+ value: =LookUp(Topic.CafeSelectionOptions, cafeId = Topic.SelectedCafeId)
+
+ - kind: SetVariable
+ id: setVariable_rRiFb8
+ variable: Topic.CafeId
+ value: =Topic.SelectedCafe.cafeId
+
+ - kind: SetVariable
+ id: setVariable_srA11r
+ variable: Topic.CafeName
+ value: =Topic.SelectedCafe.name
+
+ elseActions:
+ - kind: ConditionGroup
+ id: conditionGroup_J2gQky
+ conditions:
+ - id: conditionItem_nxnYzD
+ condition: =Topic.MatchCount = 1
+ displayName: If Cafe Match Count is equal to 1
+ actions:
+ - kind: SetVariable
+ id: setVariable_5XfLnK
+ variable: Topic.CafeId
+ value: =First(Topic.CafeSelectionOptions).cafeId
+
+ - kind: SetVariable
+ id: setVariable_fdTO9v
+ variable: Topic.CafeName
+ value: =First(Topic.CafeSelectionOptions).name
+
+ elseActions:
+ - kind: Question
+ id: YKYyKq
+ interruptionPolicy:
+ allowInterruption: true
+
+ variable: init:Topic.UserSelectedCafe
+ prompt: |-
+ I found {Topic.MatchCount} cafes matching your search term {Topic.SearchQuery}.
+
+ {Char (10) & Concat(
+ Topic.CafeSelectionOptions,
+ "- " & Text(DisplayName) & Char(10)
+ )}
+
+ Please reply with the **full name of the cafe** you're interested in.
+ defaultValue: =Blank()
+ entity:
+ kind: DynamicClosedListEntity
+ items: =Topic.CafeSelectionOptions
+
+ - kind: SetVariable
+ id: 5WcFGI
+ variable: Topic.CafeId
+ value: =Topic.UserSelectedCafe.cafeId
+
+ - kind: SetVariable
+ id: FkL5uU
+ variable: Topic.CafeName
+ value: =Topic.UserSelectedCafe.name
+
+inputType:
+ properties:
+ MatchedCafes:
+ displayName: MatchedCafes
+ description: Cafes that matched user's search term
+ type:
+ kind: Table
+ properties:
+ buildingId: String
+ buildingName: String
+ cafeId: Number
+ id: String
+ name: String
+ onlineAvailabilityCapability: String
+
+ SearchQuery:
+ displayName: SearchQuery
+ description: User's search query
+ type: String
+
+outputType:
+ properties:
+ CafeId:
+ displayName: CafeId
+ description: The ID of the cafe user selected
+ type: String
+
+ CafeName:
+ displayName: CafeName
+ description: The name of the cafe user selected
+ type: String
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningSearchCafesQuery.yaml b/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningSearchCafesQuery.yaml
new file mode 100644
index 00000000..28060f33
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningSearchCafesQuery.yaml
@@ -0,0 +1,339 @@
+kind: AdaptiveDialog
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: UserQuery
+ description: user input for searching cafes
+ entity: StringPrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+modelDescription: |-
+ This is an intelligent cafe search topic that processes natural language queries to find Microsoft campus cafes.
+
+ Features:
+ - Extracts station hints from queries
+ - Performs fuzzy matching for cafe names
+ - Returns confidence scores for search results
+ - Provides smart suggestions for ambiguous queries
+
+ Returns:
+ - High confidence (≥0.8): Direct cafe match
+ - Medium confidence (0.5-0.8): Multiple suggestions
+ - Low confidence (<0.5): No clear matches
+beginDialog:
+ kind: OnRedirect
+ id: main
+ actions:
+ - kind: BeginDialog
+ id: Cj1WJz
+ input: {}
+ dialog: msdyn_copilotforemployeeselfservice.topic.FacDiningGetCafesList
+ output:
+ binding:
+ CafesCount: Topic.CafesCount
+ CafesList: Topic.CafesList
+ CafesListResponseStatusCode: Topic.CafesListResponseStatusCode
+
+ - kind: ConditionGroup
+ id: conditionGroup_rFvnfS
+ conditions:
+ - id: conditionItem_kLmPkU
+ condition: =Topic.CafesCount > 0
+ actions:
+ - kind: SetVariable
+ id: setVariable_C5DfHd
+ variable: Topic.MatchedCafes
+ value: |-
+ =// Optimized Cafe Search Scoring Logic
+ FirstN(
+ With(
+ {
+ // Normalize query once
+ normalizedQuery: Lower(Topic.UserQuery),
+ queryWords: Split(Lower(Topic.UserQuery), " "),
+
+ // Extract query without cafe prefix
+ baseQuery: If(
+ Or(
+ StartsWith(Lower(Topic.UserQuery), "cafe "),
+ StartsWith(Lower(Topic.UserQuery), "café ")
+ ),
+ Lower(Mid(Topic.UserQuery, 6)),
+ Lower(Topic.UserQuery)
+ ),
+
+ // Pre-calculate exact matches
+ exactMatches: Filter(
+ Topic.CafesList,
+ Or(
+ Lower(name) = Lower(Topic.UserQuery),
+ And(
+ Or(
+ StartsWith(Lower(Topic.UserQuery), "cafe "),
+ StartsWith(Lower(Topic.UserQuery), "café ")
+ ),
+ Or(
+ Lower(name) = "café " & Lower(Mid(Topic.UserQuery, 6)),
+ Lower(name) = "cafe " & Lower(Mid(Topic.UserQuery, 6))
+ )
+ )
+ )
+ )
+ },
+
+ // Return exact matches with max score or perform scored search
+ If(
+ CountRows(exactMatches) > 0,
+
+ ForAll(
+ exactMatches,
+ {
+ buildingId: buildingId,
+ buildingName: buildingName,
+ cafeId: cafeId,
+ id: id,
+ name: name,
+ onlineAvailabilityCapability: onlineAvailabilityCapability,
+ relevanceScore: 500,
+ DisplayText: name & " (" & buildingName & ")",
+ Value: id
+ }
+ ),
+
+ // Scored search
+ SortByColumns(
+ Filter(
+ ForAll(
+ Topic.CafesList,
+ With(
+ {
+ lowerName: Lower(name),
+ lowerBuilding: Lower(buildingName),
+ normalizedName: Lower(Substitute(Substitute(Substitute(name, " ", ""), ":", ""), "-", "")),
+ normalizedQueryClean: Lower(Substitute(Substitute(Substitute(Topic.UserQuery, " ", ""), ":", ""), "-", ""))
+ },
+ {
+ buildingId: buildingId,
+ buildingName: buildingName,
+ cafeId: cafeId,
+ id: id,
+ name: name,
+ onlineAvailabilityCapability: onlineAvailabilityCapability,
+
+ relevanceScore:
+ // Cafe prefix pattern match (300 points)
+ If(
+ And(
+ Or(StartsWith(normalizedQuery, "cafe "), StartsWith(normalizedQuery, "café ")),
+ Or(
+ StartsWith(lowerName, "café " & baseQuery),
+ StartsWith(lowerName, "cafe " & baseQuery)
+ )
+ ),
+ 300,
+ 0
+ ) +
+
+ // Full substring match (150 points)
+ If(!IsBlank(Find(normalizedQuery, lowerName)), 150, 0) +
+
+ // Query without cafe prefix (140 points)
+ If(
+ baseQuery <> normalizedQuery && !IsBlank(Find(baseQuery, lowerName)),
+ 140,
+ 0
+ ) +
+
+ // All words found (120 points)
+ If(
+ CountIf(queryWords, !IsBlank(Value) && !IsBlank(Find(Value, lowerName))) = CountRows(queryWords),
+ 120,
+ 0
+ ) +
+
+ // Normalized match (80 points each direction)
+ If(!IsBlank(Find(normalizedQueryClean, normalizedName)), 80, 0) +
+ If(!IsBlank(Find(normalizedName, normalizedQueryClean)), 80, 0) +
+
+ // Number matching (100 points for exact, 90 with cafe prefix)
+ If(
+ !IsError(Value(Topic.UserQuery)) && name = Topic.UserQuery,
+ 100,
+ If(
+ baseQuery <> normalizedQuery && !IsError(Value(baseQuery)) && !IsBlank(Find(baseQuery, name)),
+ 90,
+ 0
+ )
+ ) +
+
+ // Partial word matches (40 points each)
+ CountIf(queryWords, !IsBlank(Value) && Len(Value) > 1 && !IsBlank(Find(Value, lowerName))) * 40 +
+
+ // Number in name (30 points each)
+ CountIf(queryWords, !IsError(Value(Value)) && !IsBlank(Find(Value, name))) * 30 +
+
+ // Building matches (25 + 10 per word)
+ If(!IsBlank(Find(normalizedQuery, lowerBuilding)), 25, 0) +
+ CountIf(queryWords, !IsBlank(Value) && Len(Value) > 2 && !IsBlank(Find(Value, lowerBuilding))) * 10,
+
+ DisplayText: name & " (" & buildingName & ")",
+ Value: id
+ }
+ )
+ ),
+ relevanceScore > 0
+ ),
+ "relevanceScore",
+ SortOrder.Descending
+ )
+ )
+ ),
+ 5
+ )
+
+ - kind: SetVariable
+ id: setVariable_eoTzvz
+ variable: Topic.MatchCount
+ value: =CountRows(Topic.MatchedCafes)
+
+ - kind: SetVariable
+ id: setVariable_8tS7of
+ variable: Topic.SearchConfidence
+ value: |-
+ =// Calculate search confidence based on match quality
+ If(
+ Topic.MatchCount = 0, 0,
+ If(
+ Topic.MatchCount = 1,
+ // Single match - confidence based on relevance score
+ Min(First(Topic.MatchedCafes).relevanceScore / 500, 1),
+
+ // Multiple matches - confidence based on score distribution
+ With(
+ {
+ topScore: First(Topic.MatchedCafes).relevanceScore,
+ secondScore: If(Topic.MatchCount > 1, Last(FirstN(Topic.MatchedCafes, 2)).relevanceScore, 0)
+ },
+
+ // High confidence if clear winner
+ If(
+ topScore > 150 && (secondScore = 0 || topScore > secondScore * 1.5),
+ 0.9,
+
+ // Medium confidence if reasonable matches
+ If(topScore > 80, 0.6, 0.3)
+ )
+ )
+ )
+ )
+
+ - kind: SetVariable
+ id: setVariable_K9k04F
+ variable: Topic.ResultType
+ value: |-
+ =If(
+ Topic.MatchCount = 0, "NoMatch",
+ If(Topic.MatchCount = 1, "SingleMatch", "MultipleMatches")
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_7YzVGE
+ conditions:
+ - id: conditionItem_LTZKnp
+ condition: =Topic.ResultType = "SingleMatch"
+ actions:
+ - kind: SetVariable
+ id: setVariable_vur73P
+ variable: Topic.CafeId
+ value: =First(Topic.MatchedCafes).id
+
+ - kind: SetVariable
+ id: setVariable_9PczAC
+ variable: Topic.CafeName
+ value: =First(Topic.MatchedCafes).name
+
+ elseActions:
+ - kind: SetVariable
+ id: setVariable_f9z6Mk
+ variable: Topic.ResultType
+ value: NoMatches
+
+ - kind: SetVariable
+ id: setVariable_GWh1JI
+ variable: Topic.MatchCount
+ value: 0
+
+ - kind: SetVariable
+ id: setVariable_NJsKeJ
+ variable: Topic.SearchConfidence
+ value: 0
+
+ - kind: SetVariable
+ id: setVariable_F9uxzv
+ variable: Topic.MatchedCafes
+ value: =Table()
+
+ - kind: SetVariable
+ id: setVariable_IuRiQ4
+ variable: Topic.CafeId
+ value: =Blank()
+
+ - kind: SetVariable
+ id: setVariable_SGzzwC
+ variable: Topic.CafeName
+ value: =Blank()
+
+inputType:
+ properties:
+ UserQuery:
+ displayName: UserQuery
+ description: user input for searching cafes
+ type: String
+
+outputType:
+ properties:
+ CafeId:
+ displayName: CafeId
+ description: Unique identifier of the matched cafe (only set for high confidence)
+ type: String
+
+ CafeName:
+ displayName: CafeName
+ description: Display name of the matched cafe (only set for high confidence)
+ type: String
+
+ MatchCount:
+ displayName: MatchCount
+ description: The number of matches that were returned by the search
+ type: Number
+
+ MatchedCafes:
+ displayName: MatchedCafes
+ type:
+ kind: Table
+ properties:
+ buildingId: String
+ buildingName: String
+ cafeId: Number
+ DisplayText: String
+ id: String
+ name: String
+ onlineAvailabilityCapability: String
+ relevanceScore: Number
+ Value: String
+
+ ResultType:
+ displayName: ResultType
+ description: The result type, "SingleMatch", "MultipleMatches", "NoMatches"
+ type: String
+
+ SearchConfidence:
+ displayName: SearchConfidence
+ description: |-
+ Confidence score (0-1) of the search result
+ - ≥0.8: High confidence, single match
+ - 0.5-0.8: Medium confidence, suggestions available
+ - <0.5: Low confidence, no clear matches
+ type: Number
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningSelectCafe.yaml b/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningSelectCafe.yaml
new file mode 100644
index 00000000..38791790
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/msdyn_copilotforemployeeselfservice.topic.FacDiningSelectCafe.yaml
@@ -0,0 +1,166 @@
+kind: AdaptiveDialog
+modelDisplayName: Fac Dining Select Cafe
+modelDescription: This topic is only triggered when redirected to from another topic. It provides the user with a list of cafes available and asks them to select one from that list. The cafe selected by the user is stored in {CafeName} output variable and returned to the calling topic.
+beginDialog:
+ kind: OnRedirect
+ id: main
+ runOnce: false
+ actions:
+ - kind: SetVariable
+ id: setVariable_fvKj9K
+ variable: Topic.CountryCode
+ value: |
+ =If(
+ And(!IsBlank(Global.ESS_UserContext_Country_Code), !IsMatch(Global.ESS_UserContext_Country_Code,""), !IsMatch(Global.ESS_UserContext_Country_Code, "''")), Global.ESS_UserContext_Country_Code
+
+ )
+
+ - kind: SetVariable
+ id: setVariable_iv7sTd
+ variable: Topic.IsCountryCodeValid
+ value: |
+ = And(!IsBlank(Topic.CountryCode), !IsMatch(Topic.CountryCode,""), !IsMatch(Topic.CountryCode, "''"))
+
+ - kind: HttpRequestAction
+ id: qTlyd8
+ url: |-
+ =Concatenate(
+ Env.fac_DiningServiceApiUrl,
+ "/api/v2/cafes/list?officeLocationRadius=50", If(Topic.IsCountryCodeValid, "&countryCode=" & Topic.CountryCode) )
+ headers:
+ clientSource: =System.Activity.ChannelId
+ Content-Type: application/json
+ x-ms-obo-scope: =Env.fac_DiningServiceApiOboScope
+
+ errorHandling:
+ kind: ContinueOnErrorBehavior
+ statusCode: Topic.GetCafesListApiResponseStatusCode
+
+ requestTimeoutInMilliseconds: 90000
+ response: Topic.CafesListResponse
+ responseSchema:
+ kind: Record
+ properties:
+ count: Number
+ items:
+ type:
+ kind: Table
+ properties:
+ buildingId: String
+ buildingName: String
+ cafeId: Number
+ coordinates:
+ type:
+ kind: Record
+ properties:
+ latitude: Number
+ longitude: Number
+
+ countryCode: String
+ currencyName: String
+ currencySymbol: String
+ disclaimer: String
+ id: String
+ imageUrl: String
+ legacyBuildingId: Number
+ location:
+ type:
+ kind: Record
+ properties:
+ coordinates:
+ type:
+ kind: Table
+ properties:
+ Value: Number
+
+ latitude: Number
+ longitude: Number
+ type: String
+
+ mealPeriods:
+ type:
+ kind: Table
+ properties:
+ customPeriod: Blank
+ endsOn: String
+ hoursOfOperation: String
+ id: String
+ name: String
+ startsOn: String
+ timeStamp: String
+
+ name: String
+ onlineAvailabilityCapability: String
+ percentOrdersForSixtyDays: Blank
+ regionId: String
+ serviceHours: String
+ timestamp: String
+
+ metadata: Blank
+
+ - kind: SetVariable
+ id: setVariable_kuwwg2
+ variable: Topic.CafesByName
+ value: "=Filter(SortByColumns(ForAll(Topic.CafesListResponse.items, { name: name }), 'name', SortOrder.Ascending), !IsBlank(name))"
+
+ - kind: AdaptiveCardPrompt
+ id: tWYjvG
+ interruptionPolicy:
+ allowInterruption: false
+ interruptionTopicListFilter:
+ kind: IncludeSelectedTopics
+ allowedInterruptTopics:
+ - msdyn_copilotforemployeeselfservice.topic.FacDiningGetMenuItems
+
+ repeatCount: 0
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "https://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "Container",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "CafeName",
+ errorMessage: "Please select a cafe",
+ spacing: "Medium",
+ label: "Please select a cafe",
+ isRequired: true,
+ choices: ForAll(Topic.CafesByName, {
+ title: name,
+ value: name
+ })
+ },
+ {
+ type: "ActionSet",
+ actions: [
+ {
+ type: "Action.Submit",
+ title: "Submit",
+ style: "positive",
+ tooltip: "Submit your cafe selection"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ output:
+ binding:
+ CafeName: Topic.CafeName
+
+ outputType:
+ properties:
+ CafeName: String
+
+inputType: {}
+outputType:
+ properties:
+ CafeName:
+ displayName: CafeName
+ description: Name of the cafe that the user selected from the provided list of cafe names
+ type: String
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/topic.yaml b/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/topic.yaml
new file mode 100644
index 00000000..430fb939
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeGetDiningMenu/topic.yaml
@@ -0,0 +1,209 @@
+kind: AdaptiveDialog
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: CafeName
+ description: The name of a Microsoft campus cafe.
+ entity: StringPrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+ - kind: AutomaticTaskInput
+ propertyName: StationName
+ description: The specific food station within a cafe (e.g., grill, salad bar, sandwich station, pizza, sushi, beverage station)
+ entity: StringPrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+modelDisplayName: Fac Dining Get Menus Query
+modelDescription: |-
+ This topic triggers when a user asks about menus for a cafe, food hall, or station. It extracts two optional inputs: cafe name {Topic.CafeName} and station name {Topic.StationName} from the user input
+
+ Example trigger phrases:
+ - "Show me the menus"
+ - "Show menus at Cafe 34"
+ - "What's available at One Esterra?"
+ - "Menus at Food Hall 9"
+ - "Show me menus at cafe 86"
+ - "Show me menus at 34 and grill"
+ - "What's available at FlatTop in cafe 86?"
+
+ If both slots are filled, show the menu. If the input is ambiguous or only one input is provided, prompt for clarification whether they meant a Cafe or a Station. Display output from {Topic.FullMenu} variable in a readable format. Do not show options to modify/add to order or menu item images. Also show the {Topic.CafeStationUrl} value as a deeplink at the end.
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent: {}
+ actions:
+ - kind: ConditionGroup
+ id: conditionGroup_ctWG9q
+ conditions:
+ - id: conditionItem_cNnDkv
+ condition: =!IsBlank(Topic.CafeName) && !IsBlank(Topic.StationName)
+
+ elseActions:
+ - kind: ConditionGroup
+ id: conditionGroup_lIH0Fy
+ conditions:
+ - id: conditionItem_ulojMj
+ condition: =IsBlank(Topic.CafeName)
+ actions:
+ - kind: BeginDialog
+ id: tRBy2i
+ input: {}
+ dialog: msdyn_copilotforemployeeselfservice.topic.FacDiningSelectCafe
+ output:
+ binding:
+ CafeName: Topic.CafeName
+
+ - kind: BeginDialog
+ id: vQljTl
+ input:
+ binding:
+ UserQuery: =Topic.CafeName
+
+ dialog: msdyn_copilotforemployeeselfservice.topic.FacDiningSearchCafesQuery
+ output:
+ binding:
+ CafeId: Topic.CafeId
+ CafeName: Topic.CafeName
+ MatchCount: Topic.MatchCount
+ MatchedCafes: Topic.MatchedCafes
+ ResultType: Topic.ResultType
+ SearchConfidence: Topic.SearchConfidence
+
+ - kind: ConditionGroup
+ id: conditionGroup_nlB1BR
+ conditions:
+ - id: conditionItem_EUeGo4
+ condition: =Topic.SearchConfidence >= 0.8
+ actions:
+ - kind: SetVariable
+ id: setVariable_6aYWPj
+ variable: Topic.CafeName
+ value: =First(Topic.MatchedCafes).name
+
+ - id: conditionItem_OI8jtL
+ condition: =Topic.MatchCount = 0
+ actions:
+ - kind: BeginDialog
+ id: Kget1R
+ input: {}
+ dialog: msdyn_copilotforemployeeselfservice.topic.FacDiningSelectCafe
+ output:
+ binding:
+ CafeName: Topic.CafeName
+
+ elseActions:
+ - kind: BeginDialog
+ id: TlDkbq
+ input:
+ binding:
+ MatchedCafes: =Topic.MatchedCafes
+ SearchQuery: =Topic.CafeName
+
+ dialog: msdyn_copilotforemployeeselfservice.topic.FacDiningHandleMultipleCafeMatches_JAK
+ output:
+ binding:
+ CafeId: Topic.CafeId
+ CafeName: Topic.CafeName
+
+ - kind: BeginDialog
+ id: FBSbkY
+ input:
+ binding:
+ CafeName: =Topic.CafeName
+
+ dialog: msdyn_copilotforemployeeselfservice.topic.FacDiningGetCafeStations
+ output:
+ binding:
+ StationId: Topic.StationId
+ StationName: Topic.StationName
+
+ - kind: BeginDialog
+ id: BCJa2r
+ input:
+ binding:
+ CafeName: =Topic.CafeName
+ StationName: =Topic.StationName
+
+ dialog: msdyn_copilotforemployeeselfservice.topic.FacDiningDisplayMenus
+ output:
+ binding:
+ CafeStationMenus: Topic.FullMenu
+
+ - kind: SetVariable
+ id: setVariable_T2Lksz
+ variable: Topic.CafeId
+ value: =First(Topic.FullMenu.items).cafeId
+
+ - kind: SetVariable
+ id: setVariable_8TRgPN
+ variable: Topic.StationId
+ value: =First(Topic.FullMenu.items).stationId
+
+ - kind: SetVariable
+ id: setVariable_fkzpWm
+ variable: Topic.CafeStationUrl
+ value: |-
+ =Concatenate(
+ Env.fac_DiningWebAppUrl,
+ "/cafe/",
+ Topic.CafeId,
+ "/station/",
+ Topic.StationId
+ )
+
+inputType:
+ properties:
+ CafeName:
+ displayName: CafeName
+ description: The name of a Microsoft campus cafe.
+ type: String
+
+ StationName:
+ displayName: StationName
+ description: The specific food station within a cafe (e.g., grill, salad bar, sandwich station, pizza, sushi, beverage station)
+ type: String
+
+outputType:
+ properties:
+ CafeStationUrl:
+ displayName: CafeStationUrl
+ description: The URL for browsing cafe stations
+ type: String
+
+ FullMenu:
+ displayName: FullMenu
+ description: The full menu for the cafe and station
+ type:
+ kind: Record
+ properties:
+ count: Number
+ items:
+ type:
+ kind: Table
+ properties:
+ cafeId: String
+ cafeName: String
+ id: String
+ isAvailableOnline: Boolean
+ items:
+ type:
+ kind: Table
+ properties:
+ calories: Number
+ category: String
+ description: String
+ id: String
+ isAvailableOnline: Boolean
+ name: String
+ price: Number
+
+ name: String
+ stationId: String
+ stationName: String
+
+ metadata: Blank
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeInviteGuest/msdyn_copilotforemployeeselfserviceInviteGuests.xml b/EmployeeSelfServiceAgent/Facilities/EmployeeInviteGuest/msdyn_copilotforemployeeselfserviceInviteGuests.xml
new file mode 100644
index 00000000..aae93e3e
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeInviteGuest/msdyn_copilotforemployeeselfserviceInviteGuests.xml
@@ -0,0 +1,10 @@
+
+ 9
+ 0
+ Fac-InviteGuests
+
+ msdyn_copilotforemployeeselfservice
+
+ 0
+ 1
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeInviteGuest/topic.yaml b/EmployeeSelfServiceAgent/Facilities/EmployeeInviteGuest/topic.yaml
new file mode 100644
index 00000000..e5903e5a
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeInviteGuest/topic.yaml
@@ -0,0 +1,465 @@
+kind: AdaptiveDialog
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: InviteOperation
+ name: InviteOperation
+ description: |-
+ InviteOperation indicates the type of invite operation the user wants to perform. If user wants to invite a guest, the InviteOperation will be "create". If user wants to modify an existing invite, the InviteOperation will be "modify". If user wants to cancel an existing invite, the InviteOperation will be "delete". If not sure of the operation the InviteOperation will be 'create'.
+ Examples
+ query - "I want to invite a guest to the office"; InviteOperation value - "create"
+ query - "Hi, I created an invite in the morning and want to make changes to the invite. Can you help me with that?"; InviteOperation value - "modify"
+ query - "Hi, I created an invite in the morning and want to cancel the invite now. Can you help me with that?"; InviteOperation - "delete"
+ query - "I want to bring my family to the office, how can I do that?"; InviteOperation value - "create"
+ entity: StringPrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 1
+ defaultValue: create
+
+ - kind: AutomaticTaskInput
+ propertyName: IsGroup
+ description: |-
+ IsGroup indicates whether the user explicitly wants to create invite for more than 1 person. If the user wants to create invite for more than 1 person, then IsGroup will be true, else false.
+ Examples
+ query - "Hi, can you help me create an invite for a group of 10 people who want to visit the office campus?"; IsGroup value - true
+ query - "i want to invite someone to compus tomorrow"; IsGroup value - false
+ entity: BooleanPrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 1
+ defaultValue: false
+
+modelDescription: |-
+ You must obey the below instructions strictly to respond back:
+ 1. This topic provides a way for users to invite guests to the office. Invoke it when users explicitly ask for inviting or registering guest or giving temporary access to the guest. Prompt examples - "How can I bring my guest to the office?", "I want to invite my wife to the office".
+ 2. For general queries use search first. Do NOT use this topic when user is looking for information OR doesn’t want to invite their guests right away. Prompt examples - “How to bring my family to office?", "Can I bring some friends to show them the Redmond campus?". In such cases, use knowledge source to respond.
+ 4. InviteOperation indicates the type of invite operation user wants to perform. Default value - 'create'.
+ 5. IsGroup indicates whether user wants to invite a group. Default value - false.
+ 6. Don't invoke this topic directly
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent: {}
+ actions:
+ - kind: SetVariable
+ id: setVariable_bg3a56
+ variable: Topic.InvitePrompt
+ value: =System.Activity.Text
+
+ - kind: ConditionGroup
+ id: conditionGroup_mY70tm
+ conditions:
+ - id: conditionItem_Px7hVx
+ condition: =Topic.ConfirmVisitCreation = false
+ actions:
+ - kind: CancelAllDialogs
+ id: J33Tbt
+
+ - id: conditionItem_6IWS10
+ condition: =Topic.InviteOperation = "modify"
+ actions:
+ - kind: SendActivity
+ id: sendActivity_G7lAXK
+ activity: Sorry, at this moment I can only help creating new invites and can’t make any changes to the ones already created.
+
+ - kind: CancelAllDialogs
+ id: 0y4pxu
+
+ - id: conditionItem_wpn1ZW
+ condition: =Topic.InviteOperation = "delete"
+ actions:
+ - kind: SendActivity
+ id: sendActivity_0o2I8w
+ activity: I'm sorry, I can't cancel invitations on your behalf yet.
+
+ - kind: CancelAllDialogs
+ id: nQsydI
+
+ - id: conditionItem_J2VrDd
+ condition: =Topic.IsGroup = true
+ actions:
+ - kind: SendActivity
+ id: sendActivity_79mKGA
+ activity: I'm sorry, I can't create group invites yet. Do you want to create individual invites instead?
+
+ - kind: CancelAllDialogs
+ id: 8wIsfB
+
+ elseActions:
+ - kind: Question
+ id: question_xeabQu
+ interruptionPolicy:
+ allowInterruption: true
+
+ variable: Topic.ConfirmVisitCreation
+ prompt: |-
+ Sure! I can help you send an invitation with instructions to get campus access.
+ Do you want to get started?
+ entity:
+ kind: BooleanPrebuiltEntity
+ sensitivityLevel: None
+
+ - kind: ConditionGroup
+ id: conditionGroup_otVyKX
+ conditions:
+ - id: conditionItem_Uaf0XW
+ condition: =Topic.ConfirmVisitCreation = true
+
+ elseActions:
+ - kind: CancelAllDialogs
+ id: 1FUATC
+
+ - kind: HttpRequestAction
+ id: U2mNrG
+ displayName: Get all buildings
+ url: =Concatenate("", "/all-buildings")
+ headers:
+ Content-Type: application/json
+ x-ms-obo-scope: =""
+
+ errorHandling:
+ kind: ContinueOnErrorBehavior
+ statusCode: Topic.BuildingFetchResponseCode
+
+ response: Global.AllBuildings
+ responseSchema:
+ kind: Table
+ properties:
+ buildingId: String
+ buildingName: String
+ gmtZone: String
+
+ - kind: SetVariable
+ id: setVariable_sMIfJD
+ variable: Topic.BuildingNameList
+ value: =ShowColumns(Global.AllBuildings, 'buildingName')
+
+ - kind: ConditionGroup
+ id: conditionGroup_wcwG6b
+ conditions:
+ - id: conditionItem_YVovSN
+ condition: =!IsBlank(Topic.BuildingFetchResponseCode)
+ actions:
+ - kind: SendActivity
+ id: sendActivity_yBh0aZ
+ activity: I’m sorry, I’m having trouble processing your request. Waiting a bit before trying again might help.
+
+ - kind: CancelAllDialogs
+ id: phCIJN
+
+ - kind: AnswerQuestionWithAI
+ id: kdksll
+ autoSend: false
+ variable: Topic.MeetingTypeAuto
+ userInput: =Topic.InvitePrompt
+ additionalInstructions: |-
+ **Instructions for Responding:**
+
+ Your task is to identify the purpose of a visitor's meeting based on the user’s input and return one of the following exact values: **Business, Interview, Personal, Classified, Contractual Work**.
+ Follow the rules below:
+ 1. If the input includes "business", "stakeholder", or suggests a work-related visit, return: Business
+ 2. If the input includes "interview", "interviewee", or suggests the visitor is coming for an interview, return: Interview
+ 3. If the input includes "friend", "family", "kids", "husband", "wife", "parents", or suggests the visitor is a personal guest, return: Personal
+ 4. If the input includes "classified", return: Classified
+ 5. If the input includes "contractual work", "contractual", "contract", or "vendor", return: Contractual Work
+ 6. If none of the above words or indications are present, return: Personal
+
+ **Important Rules:**
+ - Only respond with one of these exact values: **Business, Interview, Personal, Classified, Contractual Work**
+ - Do **not** include quotes, punctuation, or any additional text
+ - Do **not** explain or summarize
+ - Do **not** ask follow-up questions
+
+ **Examples:**
+ - Input: "My wife is coming to visit" → Output: Personal
+ - Input: "Interview candidate arriving tomorrow" → Output: Interview
+ - Input: "Vendor from Infosys visiting for contract discussion" → Output: Contractual Work
+ - Input: "Visitor for quarterly stakeholder sync" → Output: Business
+
+ - kind: AnswerQuestionWithAI
+ id: kdkslm
+ autoSend: false
+ variable: Topic.LocationAuto
+ userInput: =Topic.InvitePrompt
+ additionalInstructions: |-
+ **Instructions for Responding:**
+
+ If the user specifies an office building name or location where they want to invite their guest, then return the location. For example, if the user says - "I want to invite my friend Aakash Gupta Hyd campus 1.", then the location will be Hyd campus 1. If location is not specified then just respond with an empty string, DO NOT SAY ANYTHING ELSE.
+
+ - kind: AnswerQuestionWithAI
+ id: kdksln
+ autoSend: false
+ variable: Topic.BuildingNameAuto
+ userInput: =Topic.LocationAuto
+ additionalInstructions: |-
+ Your task is to return the closest matching building name from the {Topic.BuildingNameList} list, based on the user input.
+
+ The following rules must be strictly followed.
+ IMPORTANT: Your response must be ONLY one of the valid buildingName from Global.AllBuildings. Do NOT return anything else — no extra text, no formatting, no punctuation, no quotes. Just the building name exactly as it appears in the list.
+
+ Step 1: If the input is empty or only whitespace, return an empty string.
+
+ Step 2: If the input includes a building number, normalize it to the number and return the building name from {Topic.BuildingNameList} that exactly matches that number.
+
+ Step 3: If no number or location matches, use fuzzy matching to find the most similar building name.
+
+ Final and very important Rule: Your output must be ONLY the building names from {Topic.BuildingNameList}. Do NOT include any extra message, quote marks, labels, or explanation.
+ Examples:
+ - Input: "Register my guest for office visit at Building 1" → Output: 1
+
+ - kind: SetVariable
+ id: setVariable_K7d7Se
+ variable: Topic.BuildingIdAuto
+ value: =LookUp(Global.AllBuildings, buildingName = Topic.BuildingNameAuto, buildingId)
+
+ - kind: SetVariable
+ id: setVariable_5cKrcS
+ displayName: Set Meeting type
+ variable: Topic.MeetingType
+ value: =["Business","Interview","Personal","Classified","Contractual Work"]
+
+ - kind: AdaptiveCardPrompt
+ id: 7X2gXM
+ repeatCount: 1
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ version: "1.5",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Alright, can you please help me with the following details about your invite along with guest details?",
+ wrap: true,
+ weight: "Bolder",
+ size: "default",
+ separator: true
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "meetingType",
+ label: "Select the invite type that suits your purpose.",
+ style: "expanded",
+ isRequired: true,
+ errorMessage: "Meeting purpose selection is required",
+ value: Topic.MeetingTypeAuto,
+ choices: ForAll(
+ Topic.MeetingType,
+ {
+ title: ThisRecord.Value,
+ value: ThisRecord.Value
+ }
+ )
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "buildingName",
+ label: "Where do you want to meet your guest?",
+ placeholder: "Select a building",
+ style: "compact",
+ isRequired: true,
+ value: Topic.BuildingIdAuto,
+ errorMessage: "Building selection is required",
+ choices: ForAll(
+ Global.AllBuildings,
+ {
+ title: ThisRecord.buildingName,
+ value: ThisRecord.buildingId
+ }
+ )
+ },
+ {
+ type: "Input.Text",
+ id: "guestFirstName",
+ label: "Guest first name",
+ placeholder: "Enter here",
+ isRequired: true,
+ errorMessage: "First name is required"
+ },
+ {
+ type: "Input.Text",
+ id: "guestLastName",
+ label: "Guest last name",
+ placeholder: "Enter here",
+ isRequired: true,
+ errorMessage: "Last name is required"
+ },
+ {
+ type: "Input.Text",
+ id: "guestEmail",
+ label: "Guest email address",
+ placeholder: "Enter email address",
+ style: "Email",
+ isRequired: true,
+ regex: "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$",
+ errorMessage: "Please enter a valid email address"
+ },
+ {
+ type: "Container",
+ items: [
+ {
+ type: "TextBlock",
+ text: "Start date & time",
+ wrap: true,
+ spacing: "none"
+ },
+ {
+ type: "ColumnSet",
+ spacing: "none",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Date",
+ id: "startDate",
+ value: Text(Today(), "yyyy-mm-dd")
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Time",
+ id: "startTime"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "visitDuration",
+ label: "Duration of the visit (hours)",
+ style: "compact",
+ value: "1",
+ errorMessage: "Please select the visit duration (hours)",
+ choices: [
+ { title: "1", value: "1" },
+ { title: "2", value: "2" },
+ { title: "3", value: "3" },
+ { title: "4", value: "4" },
+ { title: "5", value: "5" },
+ { title: "6", value: "6" },
+ { title: "7", value: "7" },
+ { title: "8", value: "8" },
+ { title: "9", value: "9" },
+ { title: "10", value: "10" }
+ ]
+ }
+ ],
+ actions: [
+ {
+ type: "Action.Submit",
+ id: "createVisit",
+ title: "Submit",
+ associatedInputs: "auto"
+ }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.actionSubmitId
+ buildingName: Topic.buildingName
+ guestEmail: Topic.guestEmail
+ guestFirstName: Topic.guestFirstName
+ guestLastName: Topic.guestLastName
+ meetingType: Topic.meetingType
+ startDate: Topic.startDate
+ startTime: Topic.startTime
+ submitAction: Topic.submitAction
+ visitDuration: Topic.visitDuration
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ buildingName: String
+ guestEmail: String
+ guestFirstName: String
+ guestLastName: String
+ meetingType: String
+ startDate: String
+ startTime: String
+ submitAction: String
+ visitDuration: Number
+
+ - kind: SetVariable
+ id: setVariable_y9BRaO
+ variable: Topic.selectedBuildingTimeZone
+ value: =LookUp(Global.AllBuildings, buildingId = Topic.buildingName, gmtZone)
+
+ - kind: HttpRequestAction
+ id: WWrVO3
+ displayName: HTTP Request
+ method: Put
+ url: =Concatenate("","/GMS/invite")
+ headers:
+ Content-Type: application/json
+ x-ms-obo-scope: =""
+
+ body:
+ kind: JsonRequestContent
+ content: |-
+ ={
+ guestFirstName: Topic.guestFirstName,
+ guestLastName: Topic.guestLastName,
+ buildingName: Topic.buildingName,
+ guestEmail: Topic.guestEmail,
+ meetingType: Topic.meetingType,
+ visitDurationHours:Topic.visitDuration,
+ startDate: Topic.startDate,
+ startTime: Topic.startTime,
+ gmtZone: Topic.selectedBuildingTimeZone
+ }
+
+ errorHandling:
+ kind: ContinueOnErrorBehavior
+ statusCode: Topic.InviteVisitorsResponseCode
+
+ - kind: ConditionGroup
+ id: conditionGroup_3kWXq0
+ conditions:
+ - id: conditionItem_tiC2Vb
+ condition: =!IsBlank(Topic.InviteVisitorsResponseCode)
+ actions:
+ - kind: SendActivity
+ id: sendActivity_FZ1qor
+ activity: I’m sorry, I’m having trouble processing your request. Waiting a bit before trying again might help.
+
+ - kind: CancelAllDialogs
+ id: 0NZ6fK
+
+ - kind: SetVariable
+ id: setVariable_AQNJBh
+ variable: Topic.selectedBuildingName
+ value: =LookUp(Global.AllBuildings, buildingId = Topic.buildingName, buildingName)
+
+ - kind: SendActivity
+ id: sendActivity_cIbnAU
+ activity: |-
+ {Topic.guestFirstName}'s visit has been created.
+
+ - kind: CancelAllDialogs
+ id: OZRfY5
+
+inputType:
+ properties:
+ InviteOperation:
+ displayName: InviteOperation
+ type: String
+
+ IsGroup:
+ displayName: IsGroup
+ description: |-
+ IsGroup indicates whether the user explicitly wants to create invite for more than 1 person. If the user wants to create invite for more than 1 person, then IsGroup will be true, else false.
+ Examples
+ query - "Hi, can you help me create an invite for a group of 10 people who want to visit the office campus?"; IsGroup value - true
+ query - "I want to invite someone to compus tomorrow"; IsGroup value - false
+ type: Boolean
+
+outputType: {}
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeRegisterVehicle/msdyn_copilotforemployeeselfserviceRegisterVehicle.xml b/EmployeeSelfServiceAgent/Facilities/EmployeeRegisterVehicle/msdyn_copilotforemployeeselfserviceRegisterVehicle.xml
new file mode 100644
index 00000000..c91c65ea
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeRegisterVehicle/msdyn_copilotforemployeeselfserviceRegisterVehicle.xml
@@ -0,0 +1,11 @@
+
+ 9
+ 0
+ ESS Parking Create Vehicle Registration
+
+ msdyn_copilotforemployeeselfservice
+
+ 0
+ 1
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeRegisterVehicle/topic.yaml b/EmployeeSelfServiceAgent/Facilities/EmployeeRegisterVehicle/topic.yaml
new file mode 100644
index 00000000..965c75b6
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeRegisterVehicle/topic.yaml
@@ -0,0 +1,854 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ You must obey the below instructions strictly to respond back:
+ 1. This topic provides a way for users to register their vehicles to avail parking service at Microsoft. Invoke it when users explicitly ask for registering their vehicles. Prompt examples - "How can I register my vehicle for parking on campus?", "I want to register my vehicle".
+ 2. For general queries use search first. Do NOT use this topic when user is looking for information OR doesn’t want to register right away. Prompt examples - “How to avail parking facilities on campus?", "Can I park my vehicle on campus?". In such cases, use knowledge source to respond. Don't invoke this topic directly
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent: {}
+ actions:
+ - kind: SetVariable
+ id: setVariable_JRbdfA
+ variable: Topic.CountryCode
+ value: |
+ =If(
+ IsBlank(Global.ESS_UserContext_Country_Code) || Global.ESS_UserContext_Country_Code = """""",
+ "USA",
+ Global.ESS_UserContext_Country_Code
+ )
+
+ - kind: SetVariable
+ id: setVariable_r1hTPQ
+ variable: Topic.CountryCode
+ value: |
+ =Switch(
+ Topic.CountryCode,
+ "USA", "US",
+ "IND", "IN",
+ Topic.CountryCode
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_YDr3lq
+ conditions:
+ - id: conditionItem_qQNaEO
+ condition: =Topic.CountryCode in Env.fac_ParkingSupportedRegions
+
+ elseActions:
+ - kind: SendActivity
+ id: sendActivity_M32iRm
+ activity: I'm sorry, Currently ESS Parking Experience is only available for the Redmond and India regions.
+
+ - kind: CancelAllDialogs
+ id: v44vPr
+
+ - kind: HttpRequestAction
+ id: YgLBPA
+ displayName: Get Config Values
+ url: =Concatenate(Env.fac_ParkingServiceApiurl,"/v1.0/config/region/",Topic.CountryCode,"?label=WebUI")
+ headers:
+ x-ms-obo-scope: =Env.fac_ParkingServiceApiOboScope
+
+ errorHandling:
+ kind: ContinueOnErrorBehavior
+ statusCode: Topic.GetConfigResponseErrorCode
+ errorResponseBody: Topic.GetConfigResponseError
+
+ response: Topic.ConfigResponse
+ responseSchema:
+ kind: Record
+ properties:
+ items:
+ type:
+ kind: Table
+ properties:
+ content_type: String
+ etag: String
+ key: String
+ label: String
+ last_modified: String
+ locked: Boolean
+ tags:
+ type:
+ kind: Record
+
+ value: String
+
+ - kind: ConditionGroup
+ id: conditionGroup_rBS5DI
+ conditions:
+ - id: conditionItem_E5yK9y
+ condition: =!IsBlank(Topic.GetConfigResponseErrorCode)
+ actions:
+ - kind: SendActivity
+ id: sendActivity_RGvaUx
+ activity: I’m sorry, I’m having trouble processing your request. Waiting a bit before trying again might help.
+
+ - kind: CancelAllDialogs
+ id: ycuHBJ
+
+ - kind: SetVariable
+ id: setVariable_oCZ0bR
+ variable: Topic.VehicleTypes
+ value: |-
+ =
+ Split(
+ Coalesce(
+ Text(
+ LookUp(
+ Table(ParseJSON(LookUp(Topic.ConfigResponse.items, key = "ParkingService:Config:RegionConfig").value)),
+ Text(ThisRecord.Value.regionId) = Topic.CountryCode,
+ ThisRecord.Value.vehicleTypeList
+ )
+ ),
+ ""
+ ),
+ ","
+ )
+
+ - kind: SetVariable
+ id: setVariable_8KrqjU
+ variable: Topic.RegistrationTypes
+ value: |
+ =Split(
+ Coalesce(
+ Text(
+ LookUp(
+ Table(ParseJSON(LookUp(Topic.ConfigResponse.items, key = "ParkingService:Config:RegionConfig").value)),
+ Text(ThisRecord.Value.regionId) = Topic.CountryCode
+ ).Value.registrationTypeList
+ ),
+ ""
+ ),
+ ","
+ )
+
+ - kind: SetVariable
+ id: setVariable_ihHVJY
+ variable: Topic.FuelTypes
+ value: |
+ =Split(
+ Coalesce(
+ Text(
+ LookUp(
+ Table(ParseJSON(LookUp(Topic.ConfigResponse.items, key = "ParkingService:Config:RegionConfig").value)),
+ Text(ThisRecord.Value.regionId) = Topic.CountryCode
+ ).Value.fuelTypeList
+ ),
+ ""
+ ),
+ ","
+ )
+
+ - kind: SetVariable
+ id: setVariable_9RSwkB
+ variable: Topic.Colors
+ value: =Split(Coalesce(LookUp(Topic.ConfigResponse.items, key = "ParkingService:Registration:Color").value, ""), ",")
+
+ - kind: SetVariable
+ id: setVariable_lg86dE
+ variable: Topic.States
+ value: |
+ =Split(Coalesce(LookUp(Topic.ConfigResponse.items, key = "ParkingService:Registration:State").value, ""), ",")
+
+ - kind: SetVariable
+ id: setVariable_ga5j6l
+ variable: Topic.ParkingLocations
+ value: |
+ =Split(Coalesce(LookUp(Topic.ConfigResponse.items, key = "ParkingService:Registration:Location").value, ""), ",")
+
+ - kind: ConditionGroup
+ id: conditionGroup_7qSenN
+ conditions:
+ - id: conditionItem_DloMUX
+ condition: =Topic.CountryCode = "US"
+ actions:
+ - kind: AdaptiveCardPrompt
+ id: CAHHYJ
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ version: "1.5",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ speak: "Register your vehicle by filling out all required fields, then press the Register Vehicle button.",
+
+ body: [
+ {
+ type: "TextBlock",
+ text: "Alright, can you please help me with the following details about your vehicle?",
+ weight: "Bolder",
+ size: "default",
+ wrap: true,
+ separator:true
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "vehicleType",
+ label: "Vehicle Type",
+ isRequired: true,
+ style:"filtered",
+ errorMessage: "Select a vehicle type.",
+ choices: ForAll(
+ Topic.VehicleTypes,
+ {
+ title: ThisRecord.Value,
+ value: ThisRecord.Value
+ }
+ ),
+ value: "Car"
+ },
+ {
+ type: "ColumnSet",
+ spacing: "Medium",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "make",
+ label: "Make",
+ placeholder: "e.g. Toyota",
+ isRequired: true,
+ errorMessage: "Enter the vehicle make.",
+ regex: "^[A-Za-z0-9 ]{1,30}$",
+ maxLength: 30
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "model",
+ label: "Model",
+ placeholder: "e.g. Camry",
+ isRequired: true,
+ errorMessage: "Enter the vehicle model.",
+ regex: "^[A-Za-z0-9 ]{1,30}$",
+ maxLength: 30
+ }
+ ]
+ }
+ ]
+ },
+
+ {
+ type: "ColumnSet",
+ spacing: "Medium",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "year",
+ label: "Year",
+ choices: ForAll(
+ Sequence(52, Year(Today()) - 50),
+ {
+ title: Text(Value),
+ value: Text(Value)
+ }
+ ),
+ isRequired: true,
+ errorMessage: "Select a year.",
+ value: Text(Year(Today()))
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "ColumnSet",
+ spacing: "None",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "color",
+ label: "Color",
+ choices: ForAll(
+ Topic.Colors,
+ {
+ title: ThisRecord.Value,
+ value: ThisRecord.Value
+ }
+ ),
+ isRequired: true,
+ style:"filtered",
+ errorMessage: "Select a color."
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "auto",
+ items: [
+ {
+ type: "Icon",
+ size: "xxSmall",
+ name: "Info",
+ selectAction: {
+ type: "Action.Popover",
+ title: "Info",
+ tooltip: "Choose the colour that best matches your vehicle's exterior."
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+
+ {
+ type: "ColumnSet",
+ spacing: "Medium",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "ColumnSet",
+ spacing: "None",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "licensePlate",
+ label: "License Plate #",
+ placeholder: "e.g. ABC1234",
+ isRequired: true,
+ errorMessage: "Alphanumeric only, up to 10 characters.",
+ regex: "^[A-Za-z0-9]{1,10}$",
+ maxLength: 10
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "auto",
+ items: [
+ {
+ type: "Icon",
+ size: "xxSmall",
+ name: "Info",
+ selectAction: {
+ type: "Action.Popover",
+ title: "Info",
+ tooltip: "Alphanumeric only, no spaces or special characters, up to 10 characters."
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+
+ {
+ type: "ColumnSet",
+ spacing: "Medium",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "state",
+ label: "State/Province",
+ choices: ForAll(
+ Topic.States,
+ {
+ title: ThisRecord.Value,
+ value: ThisRecord.Value
+ }
+ ),
+ isRequired: true,
+ style:"filtered",
+ errorMessage: "Select a state or province."
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "parkingLocation",
+ label: "Parking Location",
+ choices: ForAll(
+ Topic.ParkingLocations,
+ {
+ title: ThisRecord.Value,
+ value: ThisRecord.Value
+ }
+ ),
+ isRequired: true,
+ style:"filtered",
+ errorMessage: "Choose a parking location."
+ }
+ ]
+ }
+ ]
+ },
+
+ {
+ type: "Input.ChoiceSet",
+ id: "registrationType",
+ label: "Registration Type",
+ choices: ForAll(
+ Topic.RegistrationTypes,
+ {
+ title: ThisRecord.Value,
+ value: ThisRecord.Value
+ }
+ ),
+ isRequired: true,
+ style:"filtered",
+ errorMessage: "Select a registration type.",
+ value: "Personal"
+ },
+ {
+ type: "Input.Toggle",
+ id: "policyAccepted",
+ label:"Parking Policy",
+ title: "I have read and understood the [GWS Facilities Policy on Parking and Driving on site](https://microsoft.sharepoint.com/sites/mspolicy/SitePages/PolicyProcedure.aspx?policyprocedureid=MSPOLICY-510345001-5)",
+ isRequired: true,
+ errorMessage: "You must accept the GWS Facilities Policy to proceed.",
+ valueOn: "true",
+ valueOff: "false"
+ }
+ ],
+
+ actions: [
+ {
+ type: "Action.Submit",
+ id: "submitVehicleRegistration",
+ title: "Register Vehicle",
+ associatedInputs: "auto"
+ }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.actionSubmitId
+ color: Topic.color
+ licensePlate: Topic.licensePlate
+ make: Topic.make
+ model: Topic.model
+ parkingLocation: Topic.parkingLocation
+ policyAccepted: Topic.policyAccepted
+ registrationType: Topic.registrationType
+ state: Topic.state
+ submitAction: Topic.submitAction
+ vehicleType: Topic.vehicleType
+ year: Topic.year
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ color: String
+ licensePlate: String
+ make: String
+ model: String
+ parkingLocation: String
+ policyAccepted: Boolean
+ registrationType: String
+ state: String
+ submitAction: String
+ vehicleType: String
+ year: Number
+
+ - id: conditionItem_JgQTAe
+ condition: =Topic.CountryCode = "IN"
+ actions:
+ - kind: AdaptiveCardPrompt
+ id: ah00tZ
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ version: "1.5",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ speak: "Register your vehicle by filling out all required fields, then press the Register Vehicle button.",
+
+ body: [
+ {
+ type: "TextBlock",
+ text: "Alright, can you please help me with the following details about your vehicle?",
+ weight: "Bolder",
+ size: "default",
+ wrap: true,
+ separator:true
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "vehicleType",
+ label: "Vehicle Type",
+ isRequired: true,
+ errorMessage: "Select a vehicle type.",
+ style:"filtered",
+ choices: ForAll(
+ Topic.VehicleTypes,
+ {
+ title: ThisRecord.Value,
+ value: ThisRecord.Value
+ }
+ ),
+ value: "Car"
+ },
+ {
+ type: "ColumnSet",
+ spacing: "Medium",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "make",
+ label: "Make",
+ placeholder: "e.g. Toyota",
+ isRequired: true,
+ errorMessage: "Enter the vehicle make.",
+ regex: "^[A-Za-z0-9 ]{1,30}$",
+ maxLength: 30
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "model",
+ label: "Model",
+ placeholder: "e.g. Camry",
+ isRequired: true,
+ errorMessage: "Enter the vehicle model.",
+ regex: "^[A-Za-z0-9 ]{1,30}$",
+ maxLength: 30
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "ColumnSet",
+ spacing: "Medium",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "color",
+ placeholder: "Enter vehicle color",
+ label:"Color",
+ isRequired: true,
+ errorMessage: "Enter a color."
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "registrationType",
+ label: "Registration Type",
+ choices: ForAll(
+ Topic.RegistrationTypes,
+ {
+ title: ThisRecord.Value,
+ value: ThisRecord.Value
+ }
+ ),
+ isRequired: true,
+ style:"filtered",
+ errorMessage: "Select a registration type.",
+ value: "Personal"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "Input.Text",
+ id: "licensePlate",
+ label: "Registration Number",
+ placeholder: "Enter license number",
+ isRequired: true,
+ errorMessage: "Alphanumeric only, up to 10 characters.",
+ regex: "^[A-Za-z0-9]{1,10}$",
+ maxLength: 10
+ },
+ {
+ type: "ColumnSet",
+ spacing: "Medium",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "fuelType",
+ label: "Fuel Type",
+ choices: ForAll(
+ Topic.FuelTypes,
+ {
+ title: ThisRecord.Value,
+ value: ThisRecord.Value
+ }
+ ),
+ isRequired: true,
+ style:"filtered",
+ value:"Petrol",
+ errorMessage: "Select a fuel type."
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "contactNumber",
+ label: "Contact Number",
+ placeholder: "e.g. 9876543210",
+ errorMessage: "Enter a valid contact number.",
+ regex: "^[0-9]{10}$",
+ maxLength: 10
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "ColumnSet",
+ spacing: "Medium",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "ownerFirstName",
+ label: "Owner First Name",
+ placeholder: "First name",
+ isRequired: true,
+ errorMessage: "Enter owner's first name.",
+ regex: "^[A-Za-z ]{1,50}$",
+ maxLength: 50,
+ value:Global.ESS_UserContext_Employee_Firstname
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "ownerLastName",
+ label: "Owner Last Name",
+ placeholder: "Last name",
+ isRequired: true,
+ errorMessage: "Enter owner's last name.",
+ regex: "^[A-Za-z ]{1,50}$",
+ maxLength: 50,
+ value: Global.ESS_UserContext_Employee_Lastname
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "Input.Toggle",
+ id: "disabilityRequirement",
+ title: "Do you have disability related specific requirements",
+ valueOn: "true",
+ valueOff: "false",
+ value: "false"
+ },
+ {
+ type: "Input.Toggle",
+ id: "policyAccepted",
+ title: "I have read and understood the [GWS Facilities Policy on Parking and Driving on site](https://microsoft.sharepoint.com/sites/globalsecurity/SitePages/Regions-Asia-India-Parking.aspx)",
+ isRequired: true,
+ label:"Parking Policy",
+ errorMessage: "You must accept the parking policy to proceed.",
+ valueOn: "true",
+ valueOff: "false"
+ }
+ ],
+
+ actions: [
+ {
+ type: "Action.Submit",
+ id: "submitVehicleRegistration",
+ title: "Register Vehicle",
+ associatedInputs: "auto"
+ }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.actionSubmitId
+ color: Topic.color
+ contactNumber: Topic.contactNumber
+ disabilityRequirement: Topic.disabilityRequirement
+ fuelType: Topic.fuelType
+ licensePlate: Topic.licensePlate
+ make: Topic.make
+ model: Topic.model
+ ownerFirstName: Topic.ownerFirstName
+ ownerLastName: Topic.ownerLastName
+ policyAccepted: Topic.policyAccepted
+ registrationType: Topic.registrationType
+ submitAction: Topic.submitAction
+ vehicleType: Topic.vehicleType
+ year: Topic.year
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ color: String
+ contactNumber: String
+ disabilityRequirement: Boolean
+ fuelType: String
+ licensePlate: String
+ make: String
+ model: String
+ ownerFirstName: String
+ ownerLastName: String
+ policyAccepted: Boolean
+ registrationType: String
+ submitAction: String
+ vehicleType: String
+ year: Number
+
+ elseActions:
+ - kind: CancelAllDialogs
+ id: mVwi5b
+
+ - kind: HttpRequestAction
+ id: z9HM8p
+ displayName: Create Vehicle Registration
+ method: Post
+ url: =Concatenate(Env.fac_ParkingServiceApiurl,"/v1.0/vehicle-ess/region/",Topic.CountryCode,"/registration")
+ headers:
+ Content-Type: application/json
+ x-ms-obo-scope: =Env.fac_ParkingServiceApiOboScope
+
+ body:
+ kind: JsonRequestContent
+ content: |-
+ ={
+ user:{
+ firstName: Global.ESS_UserContext_Employee_Firstname,
+ lastName: Global.ESS_UserContext_Employee_Lastname,
+ userAlias: Global.ESS_UserContext_Alias
+ },
+ vehicleRegistration:[{
+ expireInDays:0,
+ parkingLocation: If(Topic.CountryCode = "IN", "HYD", Topic.parkingLocation),
+ permitNumber:"Unassigned",
+ registrationType:Topic.registrationType,
+ contactNumber: Coalesce(Topic.contactNumber,""),
+ disability: Coalesce(Topic.disabilityRequirement,false),
+ parkingStickerCollected: false,
+ vehicleInfo:{
+ chauffer:{},
+ color:Topic.color,
+ fuelType:Coalesce(Topic.fuelType,"Unknown"),
+ licensePlate: Topic.licensePlate,
+ make:Topic.make,
+ model:Topic.model,
+ ownerFirstName:Topic.ownerFirstName,
+ ownerLastName:Topic.ownerLastName,
+ vehicleLocation:{
+ state:If(Topic.CountryCode = "IN", "TG", Topic.state)
+ },
+ vehicleType:Topic.vehicleType,
+ year: If(Topic.CountryCode = "IN", 0, Topic.year)
+ }
+ }]
+ }
+
+ errorHandling:
+ kind: ContinueOnErrorBehavior
+ statusCode: Topic.CreateVehicleRegistrationErrorResponseCode
+ errorResponseBody: Topic.CreateVehicleRegistrationErrorResponseBody
+
+ - kind: ConditionGroup
+ id: conditionGroup_eB38Qw
+ conditions:
+ - id: conditionItem_b2qhx5
+ condition: =!IsBlank(Topic.CreateVehicleRegistrationErrorResponseCode) && Topic.CreateVehicleRegistrationErrorResponseCode = 409
+ actions:
+ - kind: SendActivity
+ id: sendActivity_okgbkF
+ activity: I'm sorry, looks like this license plate number is already registered. Can you please try again.
+
+ - kind: SetVariable
+ id: setVariable_bcp4W1
+ variable: Topic.CreateVehicleRegistrationErrorResponseCode
+ value: =Blank()
+
+ - kind: SetVariable
+ id: setVariable_xDVhff
+ variable: Topic.CreateVehicleRegistrationErrorResponseBody
+ value: =Blank()
+
+ - kind: GotoAction
+ id: lTFcAb
+ actionId: setVariable_ga5j6l
+
+ - id: conditionItem_uVt3va
+ condition: =!IsBlank(Topic.CreateVehicleRegistrationErrorResponseCode)
+ actions:
+ - kind: SendActivity
+ id: sendActivity_Ee537H
+ activity: Sorry, I'm having trouble processing your request, please try again after sometime.
+
+ - kind: CancelAllDialogs
+ id: d1dVpR
+
+ - kind: SendActivity
+ id: sendActivity_Vvrqha
+ activity: Thanks for sharing the details, your vehicle is now registered. Let me know if I can help you with anything else.
+
+ - kind: CancelAllDialogs
+ id: pYAWSJ
+
+inputType: {}
+outputType: {}
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeSearchDiningStations/msdyn_copilotforemployeeselfservice.topic.FacDiningSearchStations.xml b/EmployeeSelfServiceAgent/Facilities/EmployeeSearchDiningStations/msdyn_copilotforemployeeselfservice.topic.FacDiningSearchStations.xml
new file mode 100644
index 00000000..a9aa9e76
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeSearchDiningStations/msdyn_copilotforemployeeselfservice.topic.FacDiningSearchStations.xml
@@ -0,0 +1,10 @@
+
+ 9
+ 1
+ Fac Dining Search Stations By Category
+
+ msdyn_copilotforemployeeselfservice
+
+ 0
+ 1
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Facilities/EmployeeSearchDiningStations/topic.yaml b/EmployeeSelfServiceAgent/Facilities/EmployeeSearchDiningStations/topic.yaml
new file mode 100644
index 00000000..cb9287e6
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/EmployeeSearchDiningStations/topic.yaml
@@ -0,0 +1,156 @@
+kind: AdaptiveDialog
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: StationCategory
+ description: Get specific station category/cuisine from the user input. Example if the user asks "Show me Italian food" then the StationCategory is "Italian". If the user asks "Show me American cuisine" then the StationCategory is "American". If the user asks "Show me Indian food", then the StationCategory is "Indian". When the user asks "Show me the places serving desserts", the StationCategory is "dessert".
+ entity: StringPrebuiltEntity
+ shouldPromptUser: true
+ inputSettings:
+ unrecognizedPrompt:
+ activity: Enter station category/cuisine. For example "Show me Indian food" or "Show me American cuisine"
+ mode: Strict
+
+modelDescription: |-
+ This topic lets user search for stations offering specific cuisine/category.
+ The user can say "Where can I find Italian cuisine?" or "Show me stations where they serve Asian food" or "Show me the places serving american food" etc. In these examples, Italian, Asian, and American are the cuisines/categories.
+ Extract response from variable "SearchStationsApiResponse". Provide response to the user in a human readable form. Format it properly so it looks clean and readable. Use **only** data values from the variable named "SearchStationsApiResponse."
+ If you are displaying "SearchStationsApiResponse" variable as a response, make sure to number them.
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent: {}
+ actions:
+ - kind: ConditionGroup
+ id: conditionGroup_uofKmm
+ conditions:
+ - id: conditionItem_6DTeNL
+ condition: =IsBlank(Topic.StationCategory)
+ actions:
+ - kind: SendActivity
+ id: sendActivity_1Q1Jfw
+ activity: I’m sorry, I couldn't process your request to get today’s dining information. Please visit [Microsoft Dining]({Concatenate(Env.fac_DiningWebAppUrl, "/?clientSource=", System.Activity.ChannelId)}) .
+
+ - kind: EndDialog
+ id: INSnmF
+
+ - kind: SetVariable
+ id: setVariable_NQspD3
+ variable: Topic.StationCategory
+ value: =Trim(Topic.StationCategory)
+
+ - kind: SetVariable
+ id: setVariable_kawISX
+ variable: Topic.SearchStationsApiUrl
+ value: =Concatenate(Env.fac_DiningServiceApiUrl, "/api/v2/search/stations?keyword=", Topic.StationCategory, "&ianaTimezoneId=America/Los_Angeles&select=CafeName,StationName,CafeId,StationId")
+
+ - kind: HttpRequestAction
+ id: KkQBqZ
+ url: =Topic.SearchStationsApiUrl
+ headers:
+ clientSource: =System.Activity.ChannelId
+ Content-Type: application/json
+ x-ms-obo-scope: =Env.fac_DiningServiceApiOboScope
+
+ requestTimeoutInMilliseconds: 70000
+ response: Topic.SearchStationsApiResponse
+ responseSchema:
+ kind: Table
+ properties:
+ CafeId: String
+ CafeName: String
+ CanPurchaseItemsOnline: Boolean
+ GeneralTags:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ Station_Description: String
+ StationId: String
+ StationName: String
+
+ - kind: ConditionGroup
+ id: conditionGroup_vSZaNW
+ conditions:
+ - id: conditionItem_q0OQaM
+ condition: =IsEmpty(Topic.SearchStationsApiResponse) || IsBlank(Topic.SearchStationsApiResponse)
+ actions:
+ - kind: SendActivity
+ id: sendActivity_Bo4hVW
+ activity: I'm sorry, I was unable to find any dining information for {Topic.StationCategory} cuisine. Visit here to check [Microsoft Dining]({Concatenate(Env.fac_DiningWebAppUrl, "/?clientSource=", System.Activity.ChannelId)})
+
+ - kind: EndDialog
+ id: S1EvAT
+
+ - kind: EndDialog
+ id: LxO6II
+
+ - kind: SendActivity
+ id: sendActivity_r5Xbs9
+ activity:
+ value: =Env.fac_DiningWebAppUrl
+ text:
+ - Here are some stations I found that might be serving the type of food you might like.
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "https://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ speak: "This container contains a list of cafes and stations. Use navigation keys.",
+ body: [
+ {
+ type: "Container",
+ items: ForAll(Topic.SearchStationsApiResponse,
+ {
+ type: "Container",
+ items: [
+ {
+ type: "TextBlock",
+ text: CafeName,
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true
+ },
+ {
+ type: "TextBlock",
+ text: Concatenate("[",StationName, "](",Env.fac_DiningWebAppUrl,"/cafe/",CafeId,"/station/",StationId,")")
+ }
+ ]
+ }
+ )
+ }
+ ]
+ }
+
+ - kind: EndConversation
+ id: pYEcde
+
+inputType:
+ properties:
+ StationCategory:
+ displayName: StationCategory
+ description: Get specific station category/cuisine from the user input. Example if the user asks "Show me Italian food" then the StationCategory is "Italian". If the user asks "Show me American cuisine" then the StationCategory is "American". If the user asks "Show me Indian food", then the StationCategory is "Indian". When the user asks "Show me the places serving desserts", the StationCategory is "dessert".
+ type: String
+
+outputType:
+ properties:
+ SearchStationsApiResponse:
+ displayName: SearchStationsApiResponse
+ type:
+ kind: Table
+ properties:
+ CafeId: String
+ CafeName: String
+ CanPurchaseItemsOnline: Boolean
+ GeneralTags:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ Station_Description: String
+ StationId: String
+ StationName: String
+ StationURL: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Facilities/README.md b/EmployeeSelfServiceAgent/Facilities/README.md
new file mode 100644
index 00000000..2715e25d
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Facilities/README.md
@@ -0,0 +1,8 @@
+---
+title: Facilities
+parent: Employee Self-Service
+nav_order: 2
+---
+## Facilities
+
+Topic definitions for Facilities are coming soon. Please check back later for updates.
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/README.md b/EmployeeSelfServiceAgent/README.md
new file mode 100644
index 00000000..b410ba49
--- /dev/null
+++ b/EmployeeSelfServiceAgent/README.md
@@ -0,0 +1,20 @@
+---
+title: Employee Self-Service
+nav_order: 9
+has_children: true
+has_toc: false
+description: Employee Self-Service samples for Microsoft Copilot Studio
+---
+# Employee Self-Service Agent
+
+Copilot Studio topics for employee self-service scenarios integrating with Workday and facilities management systems.
+
+> This sample is pending reorganization and will be deprecated from the top level in a future update.
+
+## Contents
+
+| Folder | Description |
+|--------|-------------|
+| [ESSEvaluationSamples/](./ESSEvaluationSamples/) | Evaluation test sets for ESS agent scenarios |
+| [Facilities/](./Facilities/) | Facilities management topics (tickets, dining, guests, vehicles) |
+| [Workday/](./Workday/) | Workday HR integration topics (employee and manager scenarios) |
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeAddDependents/README.md b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeAddDependents/README.md
new file mode 100644
index 00000000..947322ca
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeAddDependents/README.md
@@ -0,0 +1,104 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Employee Add Dependents
+
+## Overview
+
+This topic enables employees to view their existing dependents and add new dependents to their Workday profile through a conversational interface. Dependents include spouses, domestic partners, children, and other family members who may be covered under employee benefits.
+
+## Features
+
+- View existing dependents with their relationship and date of birth
+- Add new dependents (spouse, child, domestic partner, etc.)
+- Dynamic relationship type dropdown populated from Workday reference data
+- Confirmation flow showing summary of dependent details before submission
+- Form validation for required fields
+
+## Snapshots
+
+
+
+## Trigger Phrases
+
+- "Add a dependent"
+- "I want to add my child as a dependent"
+- "Add my spouse to my benefits"
+- "Register a new dependent"
+- "I need to add a family member"
+- "Show my dependents"
+
+## Files
+
+| File | Description |
+|------|-------------|
+| `topic.yaml` | Copilot Studio topic definition with conversation flow |
+| `msdyn_HRWorkdayHCMEmployeeGetDependents.xml` | XML template for fetching existing dependents |
+| `msdyn_HRWorkdayHCMEmployeeAddDependent.xml` | XML template for adding a new dependent |
+
+## Workday APIs Used
+
+| API | Purpose |
+|-----|---------|
+| `Human_Resources v45.0` | Fetch existing dependents |
+| `Benefits_Administration v45.1` | Add new dependent |
+
+## Flow Overview
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ User Triggers Topic │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Fetch Reference Data (Relationship Types) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Fetch Existing Dependents │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Display Existing Dependents (or "No dependents") │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Show Add Dependent Form (Adaptive Card) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Show Confirmation Card │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Submit to Workday │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Show Success/Error Message │
+└─────────────────────────────────────────────────────────────┘
+```
+
+## Configurations
+
+Environment makers need to configure the following in the topic:
+
+| Configuration | Description | Location in Topic |
+|---------------|-------------|-------------------|
+| **Gender Options** | Configure available gender options (Male, Female, Not_Declared) | Adaptive card dropdown |
+| **Country Codes** | Define available country codes (USA, CAN, GBR, etc.) | Adaptive card dropdown |
+| **Workday Icon** | Update the icon URL to match your organization's branding | Topic properties > Icon |
+| **Workday URL** | Set your organization's Workday tenant URL | HTTP action or connector configuration |
+
+## Dependencies
+
+- **msdyn_HRWorkdayHCMEmployeeGetDependents template**: Required for fetching existing dependents
+- **Employee Context**: Worker ID must be available in the conversation context
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeAddDependents/add_dependents.png b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeAddDependents/add_dependents.png
new file mode 100644
index 00000000..a005d28a
Binary files /dev/null and b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeAddDependents/add_dependents.png differ
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeAddDependents/msdyn_HRWorkdayHCMEmployeeAddDependent.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeAddDependents/msdyn_HRWorkdayHCMEmployeeAddDependent.xml
new file mode 100644
index 00000000..02744f36
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeAddDependents/msdyn_HRWorkdayHCMEmployeeAddDependent.xml
@@ -0,0 +1,65 @@
+
+
+
+
+ User
+
+ Template_AddDependentRequest
+ Benefits_Administration
+ v45.1
+
+
+
+ //*[local-name()='Dependent_Reference']/*[local-name()='ID' and @*[local-name()='type']='WID']/text()
+ DependentWID
+
+
+ //*[local-name()='Dependent_Reference']/*[local-name()='ID' and @*[local-name()='type']='Dependent_ID']/text()
+ DependentID
+
+
+
+
+
+
+
+
+
+ true
+ true
+ true
+
+ Dependent added via Copilot
+
+ {Employee_ID}
+
+
+
+
+
+ {Employee_ID}
+
+
+ {Relationship_Type}
+
+ true
+
+
+
+
+ {Country_Code}
+
+ {First_Name}
+ {Last_Name}
+
+
+ {Date_Of_Birth}
+
+ {Gender}
+
+
+
+
+
+
+
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeAddDependents/msdyn_HRWorkdayHCMEmployeeGetDependents.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeAddDependents/msdyn_HRWorkdayHCMEmployeeGetDependents.xml
new file mode 100644
index 00000000..7a87290b
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeAddDependents/msdyn_HRWorkdayHCMEmployeeGetDependents.xml
@@ -0,0 +1,104 @@
+
+
+
+
+ User
+
+ Template_GetDependentsRequest
+ Human_Resources
+ v42.0
+
+
+
+
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Dependent']/*[local-name()='Dependent_Reference']/*[local-name()='ID' and @*[local-name()='type']='Dependent_ID']/text()
+ DependentID
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Dependent']/*[local-name()='Dependent_Reference']/*[local-name()='ID' and @*[local-name()='type']='WID']/text()
+ DependentWID
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Person_Reference']/*[local-name()='ID' and @*[local-name()='type']='WID']/text()
+ PersonWID
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/@*[local-name()='Formatted_Name']
+ FullName
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/*[local-name()='First_Name']/text()
+ FirstName
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/*[local-name()='Last_Name']/text()
+ LastName
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Personal_Data']/*[local-name()='Personal_Information_Data']/*[local-name()='Birth_Date']/text()
+ DateOfBirth
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Personal_Data']/*[local-name()='Personal_Information_Data']//*[local-name()='Gender_Reference']/*[local-name()='ID' and @*[local-name()='type']='Gender_Code']/text()
+ Gender
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Related_Person_Relationship_Reference']/*[local-name()='ID' and @*[local-name()='type']='Related_Person_Relationship_ID']/text()
+ RelationshipTypeID
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Dependent']/*[local-name()='Dependent_Data']/*[local-name()='Full-time_Student']/text()
+ IsFullTimeStudent
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Dependent']/*[local-name()='Dependent_Data']/*[local-name()='Disabled']/text()
+ IsDisabled
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ true
+ false
+ false
+ false
+ false
+ false
+
+
+
+
+
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeAddDependents/topic.yaml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeAddDependents/topic.yaml
new file mode 100644
index 00000000..7e1e7b80
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeAddDependents/topic.yaml
@@ -0,0 +1,411 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ You will respond only to requests related to adding or managing dependents for the user making the request.
+ Dependents include spouses, domestic partners, children, and other family members who may be covered under employee benefits.
+ All dependent data is retrieved from and submitted to Workday, and pertains exclusively to the requesting user.
+ This data exclusively belongs to the user making the request. Do not respond to questions about other people's data.
+
+ Example invalid requests:
+ "Add a dependent for my manager"
+ "Update dependents for [EmployeeName]"
+ "Show dependents for my colleague"
+
+ Example valid requests:
+ "Add a dependent"
+ "I want to add my child as a dependent"
+ "Add my spouse to my benefits"
+ "Register a new dependent"
+ "I need to add a family member"
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent: {}
+
+ actions:
+ # Set Workday URL
+ - kind: SetVariable
+ id: set_workday_url
+ variable: Topic.WorkdayUrl
+ value: https://impl.workday.com//home.htmld
+
+ # Set Workday icon URL
+ - kind: SetVariable
+ id: set_workday_icon_url
+ variable: Topic.WorkdayIconUrl
+ value: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=
+
+ # Intro message
+ - kind: SetVariable
+ id: set_intro_msg_text
+ variable: Topic.introMsgText
+ value: ="Sure, I can help you with that. Here's where you can add a new dependent. You can also add a dependent on [Workday](" & Topic.WorkdayUrl & "), an HR platform your company uses."
+
+ - kind: SendActivity
+ id: intro_msg
+ activity: "{Topic.introMsgText}"
+
+ # Step 1: Fetch reference data for relationship types
+ - kind: ConditionGroup
+ id: checkRelationshipTypes
+ conditions:
+ - id: relationshipTypesUninitialized
+ condition: =IsBlank(Global.RelatedPersonRelationshipLookupTable)
+ displayName: If relationship type picklist is uninitialized
+ actions:
+ - kind: BeginDialog
+ id: fetchRelationshipTypes
+ displayName: Redirect to Workday System Get Reference Data
+ input:
+ binding:
+ referenceDataKey: Related_Person_Relationship_ID
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.GetReferenceData
+
+ # Step 2: Fetch existing dependents
+ - kind: BeginDialog
+ id: getDependents_BeginDialog
+ displayName: Get Current Dependents
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Now(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetDependents
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ConditionGroup
+ id: checkGetSuccess
+ conditions:
+ - id: getSuccessCondition
+ condition: =Topic.isSuccess = false
+ actions:
+ - kind: SendActivity
+ id: sendErrorMessage
+ activity: I encountered an error retrieving your dependent information. Please try again later or contact support.
+ - kind: EndDialog
+ id: endOnError
+
+ # Step 3: Parse the response
+ - kind: ParseValue
+ id: parseDependentsResponse
+ displayName: Parse dependents response
+ variable: Topic.dependentsRecord
+ valueType:
+ kind: Record
+ properties:
+ DependentID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ DependentWID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ PersonWID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ FullName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ FirstName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ LastName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ DateOfBirth:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ Gender:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ RelationshipTypeID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ IsFullTimeStudent:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ IsDisabled:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ value: =Topic.workdayResponse
+
+ # Step 4: Transform to table (XPath already filters to only actual dependents)
+ - kind: SetVariable
+ id: setVariable_transformDependents
+ displayName: Transform dependents to table
+ variable: Topic.dependentsTable
+ value: |-
+ =ForAll(
+ Sequence(CountRows(Topic.dependentsRecord.DependentID)),
+ {
+ DependentID: Last(FirstN(Topic.dependentsRecord.DependentID, Value)).Value,
+ DependentWID: Last(FirstN(Topic.dependentsRecord.DependentWID, Value)).Value,
+ PersonWID: Last(FirstN(Topic.dependentsRecord.PersonWID, Value)).Value,
+ FullName: Last(FirstN(Topic.dependentsRecord.FullName, Value)).Value,
+ FirstName: Last(FirstN(Topic.dependentsRecord.FirstName, Value)).Value,
+ LastName: Last(FirstN(Topic.dependentsRecord.LastName, Value)).Value,
+ DateOfBirth: Last(FirstN(Topic.dependentsRecord.DateOfBirth, Value)).Value,
+ Gender: Last(FirstN(Topic.dependentsRecord.Gender, Value)).Value,
+ RelationshipType: LookUp(Global.RelatedPersonRelationshipLookupTable, ID = Last(FirstN(Topic.dependentsRecord.RelationshipTypeID, Value)).Value).Referenced_Object_Descriptor,
+ RelationshipTypeID: Last(FirstN(Topic.dependentsRecord.RelationshipTypeID, Value)).Value,
+ IsFullTimeStudent: Last(FirstN(Topic.dependentsRecord.IsFullTimeStudent, Value)).Value = "1",
+ IsDisabled: Last(FirstN(Topic.dependentsRecord.IsDisabled, Value)).Value = "1"
+ }
+ )
+
+ # Step 5: Collect dependent information using Adaptive Card
+ - kind: AdaptiveCardPrompt
+ id: collectDependentCard
+ displayName: Collect dependent information
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Add a new dependent",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "TextBlock",
+ text: "Fields marked with * are required.",
+ wrap: true,
+ size: "Small",
+ spacing: "Small"
+ },
+ {
+ type: "Input.Text",
+ id: "firstName",
+ label: "First name",
+ placeholder: "Enter first name",
+ isRequired: true,
+ errorMessage: "First name is required."
+ },
+ {
+ type: "Input.Text",
+ id: "lastName",
+ label: "Last name",
+ placeholder: "Enter last name",
+ isRequired: true,
+ errorMessage: "Last name is required."
+ },
+ {
+ type: "Input.Date",
+ id: "dateOfBirth",
+ label: "Date of birth",
+ isRequired: true,
+ errorMessage: "Date of birth is required."
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "gender",
+ label: "Gender",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a gender.",
+ placeholder: "Select gender",
+ choices: [
+ { title: "Male", value: "Male" },
+ { title: "Female", value: "Female" },
+ { title: "Not declared", value: "Not_Declared" }
+ ]
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "relationshipType",
+ label: "Relationship to employee",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a relationship.",
+ placeholder: "Select relationship",
+ choices: ForAll(Global.RelatedPersonRelationshipLookupTable, { title: ThisRecord.Referenced_Object_Descriptor, value: ThisRecord.ID })
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "country",
+ label: "Country",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a country.",
+ value: "USA",
+ choices: [
+ { title: "United States", value: "USA" },
+ { title: "Canada", value: "CAN" },
+ { title: "United Kingdom", value: "GBR" },
+ { title: "India", value: "IND" },
+ { title: "Australia", value: "AUS" },
+ { title: "Germany", value: "DEU" },
+ { title: "France", value: "FRA" }
+ ]
+ }
+ ],
+ actions: [
+ { type: "Action.Submit", title: "Submit", id: "Submit", data: { actionSubmitId: "Submit" } },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", data: { actionSubmitId: "Cancel" }, associatedInputs: "none" }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.formActionId
+ firstName: Topic.firstName
+ lastName: Topic.lastName
+ dateOfBirth: Topic.dateOfBirth
+ gender: Topic.gender
+ relationshipType: Topic.relationshipType
+ country: Topic.country
+ outputType:
+ properties:
+ actionSubmitId: String
+ firstName: String
+ lastName: String
+ dateOfBirth: String
+ gender: String
+ relationshipType: String
+ country: String
+
+ # Step 7: Handle cancel
+ - kind: ConditionGroup
+ id: checkCancelAction
+ conditions:
+ - id: cancelCondition
+ condition: =Topic.formActionId = "Cancel"
+ actions:
+ - kind: SendActivity
+ id: sendCancelMessage
+ activity: Request cancelled. No dependent was added. Is there anything else I can help you with?
+ - kind: CancelAllDialogs
+ id: cancelAll
+
+ # Step 8: Validate required fields
+ - kind: ConditionGroup
+ id: validateFields
+ conditions:
+ - id: missingFieldsCondition
+ condition: =IsBlank(Topic.firstName) || IsBlank(Topic.lastName) || IsBlank(Topic.dateOfBirth) || IsBlank(Topic.gender) || IsBlank(Topic.relationshipType) || IsBlank(Topic.country)
+ actions:
+ - kind: SendActivity
+ id: sendValidationError
+ activity: Please fill in all required fields (first name, last name, date of birth, gender, relationship, and country).
+ - kind: EndDialog
+ id: endOnValidationError
+
+ # Step 9: Set relationship type ID (already comes from lookup table)
+ - kind: SetVariable
+ id: mapRelationshipType
+ displayName: Set relationship type ID
+ variable: Topic.relationshipTypeId
+ value: =Topic.relationshipType
+
+ # Step 10: Call Add Dependent API
+ - kind: BeginDialog
+ id: addDependent_BeginDialog
+ displayName: Add Dependent to Workday
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{First_Name}"",""value"":""" & Topic.firstName & """},{""key"":""{Last_Name}"",""value"":""" & Topic.lastName & """},{""key"":""{Date_Of_Birth}"",""value"":""" & Topic.dateOfBirth & """},{""key"":""{Gender}"",""value"":""" & Topic.gender & """},{""key"":""{Relationship_Type}"",""value"":""" & Topic.relationshipTypeId & """},{""key"":""{Country_Code}"",""value"":""" & Topic.country & """}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeAddDependent
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.addErrorResponse
+ isSuccess: Topic.addIsSuccess
+ workdayResponse: Topic.addWorkdayResponse
+
+ # Step 11: Show result
+ - kind: ConditionGroup
+ id: checkAddSuccess
+ conditions:
+ - id: addSuccessCondition
+ condition: =Topic.addIsSuccess = true
+ actions:
+ - kind: SendActivity
+ id: sendSuccessIntroMessage
+ activity: Great! You've added a new dependent.
+
+ - kind: SetVariable
+ id: setOtherDependentsList
+ variable: Topic.otherDependentsList
+ value: =If(CountRows(Topic.dependentsTable) > 0, Concat(Topic.dependentsTable, FullName, ", "), "None")
+
+ - kind: SendActivity
+ id: sendSuccessMessage
+ activity:
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "✅ Your new dependent was added",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "FactSet",
+ spacing: "Medium",
+ facts: [
+ { title: "Name", value: Topic.firstName & " " & Topic.lastName },
+ { title: "Date of birth", value: Topic.dateOfBirth },
+ { title: "Gender", value: Topic.gender },
+ { title: "Relationship to employee", value: LookUp(Global.RelatedPersonRelationshipLookupTable, ID = Topic.relationshipType).Referenced_Object_Descriptor },
+ { title: "Other dependents", value: Topic.otherDependentsList }
+ ]
+ }
+ ]
+ }
+
+ - kind: CancelAllDialogs
+ id: endOnSuccess
+
+ elseActions:
+ - kind: SendActivity
+ id: sendAddErrorMessage
+ activity: |-
+ There was an error adding the dependent. Please try again later or contact HR support.
+
+ **Error details:** {Topic.addErrorResponse}
+
+ - kind: CancelAllDialogs
+ id: endOnError2
+
+inputType: {}
+outputType:
+ properties:
+ addIsSuccess:
+ displayName: Add Success
+ type: Boolean
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeGetVacationBalance/README.md b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeGetVacationBalance/README.md
new file mode 100644
index 00000000..9dee802a
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeGetVacationBalance/README.md
@@ -0,0 +1,98 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Get Vacation Balance
+
+## Overview
+
+This scenario allows employees to check their time off balances from Workday. It retrieves vacation, sick leave, floating holiday, and other plan balances for the requesting user.
+
+The `EmployeeGetVacationBalance` topic allows employees to:
+- **View** their current time off balances across all plans
+- **Select** a specific as-of date to check historical or future balances
+- **Receive** AI-formatted responses with balance details
+
+## Features
+
+- **Date Selection**: Prompts user to select an as-of date via Adaptive Card, defaults to today
+- **Multiple Plan Support**: Returns balances for all time off plans (vacation, sick, floating holiday, etc.)
+- **AI-Formatted Response**: Uses generative AI to format the balance response in a friendly, conversational way
+- **Automatic Date Extraction**: If user includes a date in their request, it's automatically extracted
+
+## Snapshots
+
+### Date Selection Card
+
+
+## Trigger Phrases
+
+- "What is my vacation balance?"
+- "How much time off can I take?"
+- "What is my Workday vacation balance?"
+- "Check my PTO balance"
+- "How many sick days do I have left?"
+- "Show me my time off balance as of January 1st"
+
+## Files
+
+| File | Description |
+|------|-------------|
+| `topic.yaml` | Main topic definition with conversation flow and Adaptive Card |
+| `msdyn_HRWorkdayHCMEmployeeGetVacationBalance.xml` | XML template for the Get_Time_Off_Plan_Balances API request |
+
+## Workday APIs Used
+
+| API | Service | Version | Purpose |
+|-----|---------|---------|---------|
+| `Get_Time_Off_Plan_Balances` | Absence_Management | v42.0 | Retrieve employee's time off plan balances |
+
+## Flow Overview
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ User Triggers Topic │
+│ "What is my vacation balance?" │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+ ┌───────────────┴───────────────┐
+ │ Date provided in request? │
+ └───────────────┬───────────────┘
+ │ │
+ Yes No
+ │ │
+ ▼ ▼
+ ┌───────────────────────┐ ┌─────────────────────┐
+ │ Use extracted date │ │ Show Date Picker │
+ │ │ │ Adaptive Card │
+ └───────────────────────┘ │ (defaults to today)│
+ │ └──────────┬──────────┘
+ │ │
+ └────────────┬───────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Call Workday API │
+│ (Get_Time_Off_Plan_Balances) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ AI Formats Response │
+│ (Friendly message with balances by plan) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Display to User │
+│ "As of [date], here are your balances: │
+│ • Vacation: X hours │
+│ • Sick Leave: Y hours" │
+└─────────────────────────────────────────────────────────────┘
+```
+
+## Dependencies
+
+- `msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution` - For API execution
+- `Global.ESS_UserContext_Employee_Id` - Current user's employee ID
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeGetVacationBalance/get_vacation_balance.png b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeGetVacationBalance/get_vacation_balance.png
new file mode 100644
index 00000000..4d53f446
Binary files /dev/null and b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeGetVacationBalance/get_vacation_balance.png differ
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeGetVacationBalance/msdyn_HRWorkdayHCMEmployeeGetVacationBalance.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeGetVacationBalance/msdyn_HRWorkdayHCMEmployeeGetVacationBalance.xml
new file mode 100644
index 00000000..ab2b9b4f
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeGetVacationBalance/msdyn_HRWorkdayHCMEmployeeGetVacationBalance.xml
@@ -0,0 +1,85 @@
+
+
+
+
+ User
+
+ msdyn_HRWorkdayHCMEmployeeGetVacationBalance
+ Absence_Management
+ v42.0
+
+
+
+
+ Employee_ID
+ {Employee_ID}
+
+
+ As_Of_Effective_Date
+ {As_Of_Effective_Date}
+
+
+
+
+
+
+ RemainingBalance
+
+ //*[local-name()='Time_Off_Plan_Balance_Record']
+ /*[local-name()='Time_Off_Plan_Balance_Position_Record']
+ /*[local-name()='Time_Off_Plan_Balance']/text()
+
+
+
+
+
+ PlanDescriptor
+
+ //*[local-name()='Time_Off_Plan_Balance_Record']
+ /*[local-name()='Time_Off_Plan_Reference']/@*[local-name()='Descriptor']
+
+
+
+
+
+ PlanID
+
+ //*[local-name()='Time_Off_Plan_Balance_Record']
+ /*[local-name()='Time_Off_Plan_Reference']
+ /*[local-name()='ID'][@*[local-name()='type']='Absence_Plan_ID']/text()
+
+
+
+
+
+ UnitOfTime
+
+ //*[local-name()='Time_Off_Plan_Balance_Record']
+ /*[local-name()='Unit_of_Time_Reference']/@*[local-name()='Descriptor']
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+
+ {As_Of_Effective_Date}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeGetVacationBalance/topic.yaml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeGetVacationBalance/topic.yaml
new file mode 100644
index 00000000..ea0eb837
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeGetVacationBalance/topic.yaml
@@ -0,0 +1,142 @@
+kind: AdaptiveDialog
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: AsOfEffectiveDate
+ description: The as-of effective date (YYYY-MM-DD) to use when retrieving vacation balances. If the user includes a date in their prompt, the NLU should populate this automatically.
+ entity: DateTimePrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+modelDescription: |-
+ You will respond to requests about the total time off balance of the user making the request. This data is retrieved from Workday. You will respond to the user based on which piece of data they are asking for. This data exclusively belongs to the user making the request. There is no data for anyone else. Do not respond to questions about other people's data.
+
+ Example invalid requests:
+ "What is my manager's vacation balance?"
+ "What is my sister's vacation balance?"
+
+ Example valid requests:
+ "What is my vacation balance?"
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - What is my vacation balance?
+ - How much time off can I take?
+ - what is my workday vacation balance?
+
+ actions:
+ - kind: SetVariable
+ id: set_workday_url
+ variable: Topic.WorkdayUrl
+ value: https://impl.workday.com//home.htmld
+
+ - kind: SetVariable
+ id: set_intro_msg_text
+ variable: Topic.introMsgText
+ value: ="Sure, here's what I found on your time off balance from [Workday](" & Topic.WorkdayUrl & "), an HR platform your company uses."
+
+ - kind: SendActivity
+ id: intro_msg
+ activity: "{Topic.introMsgText}"
+
+ - kind: SetVariable
+ id: setVariable_8qSSM1
+ variable: Topic.date
+ value: =Text(Today(), "yyyy-MM-dd")
+
+ - kind: ConditionGroup
+ id: ask_effective_date_if_blank
+ conditions:
+ - id: isBlank
+ condition: =IsBlank(Topic.AsOfEffectiveDate)
+ actions:
+ - kind: AdaptiveCardPrompt
+ id: askDateCard1
+ displayName: Select As-Of Date
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "https://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Which date would you like to check your vacation balance for?",
+ weight: "Bolder",
+ wrap: true
+ },
+ {
+ type: "Input.Date",
+ id: "asOfDate",
+ label: "Select date",
+ value: Topic.date
+ }
+ ],
+ actions: [
+ {
+ type: "Action.Submit",
+ title: "Submit"
+ }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.actionSubmitId
+ asOfDate: Topic.asOfDate
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ asOfDate: Date
+
+ - kind: SetVariable
+ id: ensure_effective_date2
+ variable: Topic.AsOfEffectiveDate
+ value: =If(IsBlank(Topic.AsOfEffectiveDate), DateTimeValue(Topic.asOfDate), Topic.AsOfEffectiveDate)
+
+ - kind: BeginDialog
+ id: Y74Om43
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Topic.AsOfEffectiveDate, "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetVacationBalance
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: AnswerQuestionWithAI
+ id: igank32
+ autoSend: false
+ variable: Topic.VacationBalance
+ userInput: =Topic.workdayResponse
+ additionalInstructions: Do not show citation. Display each vacation balances in a separate line and highlight vacation name. Only reply back with the Vacation Balance. Format the response in in friendly way and make sure to tie it back directly to the orginal question of the user. ({System.Activity.Text}). Display ({Topic.asOfDate}) with the message as of. Never include the "Plan ID".
+
+inputType:
+ properties:
+ AsOfEffectiveDate:
+ displayName: AsOfEffectiveDate
+ description: The effective date to use when retrieving vacation balances (YYYY-MM-DD). Defaults to today's date if not provided.
+ type: DateTime
+
+outputType:
+ properties:
+ finalizedData:
+ displayName: finalizedData
+ type:
+ kind: Record
+ properties:
+ CostCenterCode: String
+ CostCenterName: String
+ EmployeeName: String
+
+ VacationBalance:
+ displayName: VacationBalance
+ type: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/README.md b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/README.md
new file mode 100644
index 00000000..248e2578
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/README.md
@@ -0,0 +1,93 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Employee Update Residential Address
+
+## Overview
+
+This topic enables employees to manage their home/residential address in Workday through the Copilot agent. It retrieves the employee's current home addresses, allows them to select which one to update, add a new address, or modify an existing one, and submits the changes via the Workday Change Home Contact Information API.
+
+## Features
+
+- View current home addresses with primary address marked
+- Update any field of an existing home address
+- Add a new home address when no addresses exist or user wants to add another
+- Set or change which address is the primary home address
+- Form pre-population with current address values
+
+## Snapshots
+
+
+
+## Trigger Phrases
+
+- "Update my home address"
+- "I want to update my residential address"
+- "Change my address"
+- "Update my street address"
+- "I moved to a new address"
+
+## Files
+
+| File | Description |
+|------|-------------|
+| `topic.yaml` | Copilot Studio topic definition with conversation flow |
+| `msdyn_GetResidentialAddress_Template.xml` | XML template to retrieve current home addresses |
+| `msdyn_UpdateResidentialAddress_Template.xml` | XML template to update an existing home address |
+| `msdyn_AddResidentialAddress_Template.xml` | XML template to add a new home address |
+
+## Workday APIs Used
+
+| API | Purpose |
+|-----|---------|
+| `Get_Workers` | Retrieve current home address information |
+| `Change_Home_Contact_Information` | Add or update home address |
+
+## Flow Overview
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ User Triggers Topic │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Fetch Current Home Addresses │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Show Selection (Multiple) or Auto-Select (Single) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Collect New Address via Adaptive Card Form │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Submit to Workday │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Show Success/Error Message │
+└─────────────────────────────────────────────────────────────┘
+```
+
+## Configurations
+
+Environment makers need to configure the following in the topic:
+
+| Configuration | Description | Location in Topic |
+|---------------|-------------|-------------------|
+| **Countries** | Available country options (USA, CAN, GBR, etc.) | Adaptive card dropdown |
+| **States/Provinces** | Available state/province codes per country | Adaptive card dropdown |
+| **Workday Icon** | Update the icon URL to match your organization's branding | Topic properties > Icon |
+| **Workday URL** | Set your organization's Workday tenant URL | HTTP action or connector configuration |
+
+## Dependencies
+
+- **Employee Context**: Worker ID must be available in the conversation context
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_AddResidentialAddress_Template.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_AddResidentialAddress_Template.xml
new file mode 100644
index 00000000..74699815
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_AddResidentialAddress_Template.xml
@@ -0,0 +1,69 @@
+
+
+
+
+ User
+
+ Template_AddResidentialAddressRequest
+ Human_Resources
+ v42.0
+
+
+
+ //*[local-name()='Event_Reference']/*[local-name()='ID' and @*[local-name()='type']='WID']/text()
+ EventWID
+
+
+
+
+
+
+
+
+
+ false
+ true
+ true
+
+ New address added via Copilot
+
+ {Employee_ID}
+
+
+
+
+
+ {Employee_ID}
+
+ {Event_Effective_Date}
+
+
+
+
+
+ {Country_Code}
+
+
+ {State_Province_Code}
+
+ {Address_Line_1}
+ {Address_Line_2}
+ {Postal_Code}
+ {City}
+
+
+
+
+ HOME
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_GetResidentialAddress_Template.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_GetResidentialAddress_Template.xml
new file mode 100644
index 00000000..e3424dc9
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_GetResidentialAddress_Template.xml
@@ -0,0 +1,106 @@
+
+
+
+
+ User
+
+ Template_GetResidentialAddressRequest
+ Human_Resources
+ v42.0
+
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/@*[local-name()='Formatted_Address']
+ FormattedAddress
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Address_ID']/text()
+ AddressID
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Country_Reference']/*[local-name()='ID' and @*[local-name()='type']='ISO_3166-1_Alpha-3_Code']/text()
+ CountryCode
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Country_Region_Reference']/*[local-name()='ID' and @*[local-name()='type']='Country_Region_ID']/text()
+ StateProvinceCode
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Municipality']/text()
+ City
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Postal_Code']/text()
+ PostalCode
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Address_Line_Data' and @*[local-name()='Type']='ADDRESS_LINE_1']/text()
+ AddressLine1
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Address_Line_Data' and @*[local-name()='Type']='ADDRESS_LINE_2']/text()
+ AddressLine2
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Usage_Data']/*[local-name()='Type_Data']/@*[local-name()='Primary']
+ IsPrimary
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Usage_Data']/*[local-name()='Type_Data']/*[local-name()='Type_Reference']/*[local-name()='ID' and @*[local-name()='type']='Communication_Usage_Type_ID']/text()
+ UsageType
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+ true
+ false
+ false
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+
+
+
+
+
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_UpdateResidentialAddress_Template.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_UpdateResidentialAddress_Template.xml
new file mode 100644
index 00000000..6110c982
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_UpdateResidentialAddress_Template.xml
@@ -0,0 +1,71 @@
+
+
+
+
+ User
+
+ Template_UpdateResidentialAddressRequest
+ Human_Resources
+ v42.0
+
+
+
+ //*[local-name()='Event_Reference']/*[local-name()='ID' and @*[local-name()='type']='WID']/text()
+ EventWID
+
+
+
+
+
+
+
+
+
+ false
+ true
+ true
+
+ Address updated via Copilot
+
+ {Employee_ID}
+
+
+
+
+
+ {Employee_ID}
+
+ {Event_Effective_Date}
+
+
+
+
+
+ {Country_Code}
+
+
+ {State_Province_Code}
+
+ {Address_Line_1}
+ {Address_Line_2}
+ {Postal_Code}
+ {City}
+
+
+
+
+ HOME
+
+
+
+
+ {Address_ID}
+
+
+
+
+
+
+
+
+
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/topic.yaml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/topic.yaml
new file mode 100644
index 00000000..597a86ba
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/topic.yaml
@@ -0,0 +1,710 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ You will respond only to requests related to updating the residential/home address of the user making the request. All address data is retrieved from and updated through Workday, and pertains exclusively to the requesting user. This topic may also prompt the user for input if they are requesting to make a change to their own home address. This data exclusively belongs to the user making the request. There is no data for anyone else. Do not respond to questions about other people's data.
+
+ Example invalid requests:
+ "Update address for my manager"
+ "Update my sister's home address"
+ "Update [EmployeeName]'s address"
+ "Update address for [EmployeeName]"
+
+ Example valid requests:
+ "Update my home address"
+ "I want to update my residential address"
+ "Change my address"
+ "Update my street address"
+ "I moved to a new address"
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent: {}
+
+ actions:
+ # Set Workday URL
+ - kind: SetVariable
+ id: set_workday_url
+ variable: Topic.WorkdayUrl
+ value: https://impl.workday.com//home.htmld
+
+ # Set Workday icon URL
+ - kind: SetVariable
+ id: set_workday_icon_url_init
+ variable: Topic.WorkdayIconUrl
+ value: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=
+
+ # Intro message
+ - kind: SetVariable
+ id: set_intro_msg_text
+ variable: Topic.introMsgText
+ value: ="Sure, I can help you with that. I found your address information in [Workday](" & Topic.WorkdayUrl & "), an HR platform your company uses."
+
+ - kind: SendActivity
+ id: intro_msg
+ activity: "{Topic.introMsgText}"
+
+ # Step 1: Get current residential address
+ - kind: BeginDialog
+ id: getAddress_BeginDialog
+ displayName: Get Current Residential Address
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Now(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetResidentialAddress
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ConditionGroup
+ id: checkGetSuccess
+ conditions:
+ - id: getSuccessCondition
+ condition: =Topic.isSuccess = false
+ actions:
+ - kind: SendActivity
+ id: sendErrorMessage
+ activity: I encountered an error retrieving your current address. Please try again later or contact support.
+
+ - kind: EndDialog
+ id: endOnError
+
+ # Step 2: Parse the response
+ - kind: ParseValue
+ id: parseAddressResponse
+ displayName: Parse address response
+ variable: Topic.addressRecord
+ valueType:
+ kind: Record
+ properties:
+ FormattedAddress:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ AddressID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ CountryCode:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ StateProvinceCode:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ City:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ PostalCode:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ AddressLine1:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ AddressLine2:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ IsPrimary:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ UsageType:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ value: =Topic.workdayResponse
+
+ # Step 3: Transform to table and filter HOME addresses
+ - kind: SetVariable
+ id: setVariable_transformAddresses
+ displayName: Transform addresses to table
+ variable: Topic.addressTable
+ value: |-
+ =ForAll(
+ Sequence(CountRows(Topic.addressRecord.AddressID)),
+ {
+ AddressID: Last(FirstN(Topic.addressRecord.AddressID, Value)).Value,
+ FormattedAddress: Last(FirstN(Topic.addressRecord.FormattedAddress, Value)).Value,
+ CountryCode: Last(FirstN(Topic.addressRecord.CountryCode, Value)).Value,
+ StateProvinceCode: Last(FirstN(Topic.addressRecord.StateProvinceCode, Value)).Value,
+ City: Last(FirstN(Topic.addressRecord.City, Value)).Value,
+ PostalCode: Last(FirstN(Topic.addressRecord.PostalCode, Value)).Value,
+ AddressLine1: Last(FirstN(Topic.addressRecord.AddressLine1, Value)).Value,
+ AddressLine2: Last(FirstN(Topic.addressRecord.AddressLine2, Value)).Value,
+ IsPrimary: If(Last(FirstN(Topic.addressRecord.IsPrimary, Value)).Value = "1", true, false),
+ UsageType: Last(FirstN(Topic.addressRecord.UsageType, Value)).Value
+ }
+ )
+
+ # Filter to only HOME addresses
+ - kind: SetVariable
+ id: setVariable_homeAddresses
+ displayName: Filter to HOME addresses only
+ variable: Topic.homeAddresses
+ value: =Filter(Topic.addressTable, UsageType = "HOME")
+
+ # Step 4: Check if user has existing home addresses
+ - kind: ConditionGroup
+ id: checkExistingAddresses
+ conditions:
+ - id: noAddressesCondition
+ condition: =CountRows(Topic.homeAddresses) = 0
+ actions:
+ - kind: SendActivity
+ id: sendNoAddressMessage
+ activity: You don't have a home address on file. Would you like to add a new one?
+
+ - kind: SetVariable
+ id: setIsNewAddress
+ variable: Topic.isNewAddress
+ value: =true
+
+ - kind: SetVariable
+ id: setSelectedAddressID_New
+ variable: Topic.selectedAddressID
+ value: =""
+
+ elseActions:
+ - kind: SetVariable
+ id: setIsExistingAddress
+ variable: Topic.isNewAddress
+ value: =false
+
+ # Check if single address (auto-select) or multiple (show selection)
+ - kind: ConditionGroup
+ id: checkSingleOrMultipleAddresses
+ conditions:
+ - id: singleAddressCondition
+ condition: =CountRows(Topic.homeAddresses) = 1
+ displayName: Single address - auto-select
+ actions:
+ # Auto-select the only address
+ - kind: SetVariable
+ id: autoSelectAddress
+ displayName: Auto-select single address
+ variable: Topic.selectedAddress
+ value: =First(Topic.homeAddresses)
+
+ - kind: SetVariable
+ id: autoSetAddressID
+ variable: Topic.selectedAddressID
+ value: =Topic.selectedAddress.AddressID
+
+ - kind: SendActivity
+ id: sendCurrentAddressMsg
+ activity: |-
+ I found your current home address:
+
+ 📍 **{Topic.selectedAddress.FormattedAddress}**
+
+ elseActions:
+ # Multiple addresses - show selection card
+ - kind: SetVariable
+ id: buildAddressChoices
+ displayName: Build address selection choices
+ variable: Topic.addressChoices
+ value: |
+ =ForAll(
+ Topic.homeAddresses,
+ {
+ title: ThisRecord.AddressLine1 & ", " & ThisRecord.City & ", " & ThisRecord.StateProvinceCode & " " & ThisRecord.PostalCode & " (" & If(ThisRecord.IsPrimary, "Primary", "Home") & ")",
+ value: ThisRecord.AddressID
+ }
+ )
+
+ # Show address selection card
+ - kind: AdaptiveCardPrompt
+ id: selectAddressCard
+ displayName: Select address to update
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "auto",
+ items: [
+ {
+ type: "Image",
+ url: Topic.WorkdayIconUrl,
+ style: "RoundedCorners",
+ size: "Small",
+ height: "20px",
+ width: "20px"
+ }
+ ],
+ verticalContentAlignment: "Center"
+ },
+ {
+ type: "Column",
+ width: "auto",
+ items: [
+ {
+ type: "TextBlock",
+ text: "",
+ size: "Small",
+ weight: "Bolder"
+ }
+ ],
+ verticalContentAlignment: "Center",
+ spacing: "Small"
+ }
+ ]
+ },
+ {
+ type: "TextBlock",
+ text: "Select an address to update",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "TextBlock",
+ text: "You have " & CountRows(Topic.homeAddresses) & " addresses. Select to update or add a new address.",
+ wrap: true,
+ spacing: "Small"
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "selectedAddress",
+ label: "Address",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select an address.",
+ choices: ForAll(Topic.addressChoices, { title: ThisRecord.title, value: ThisRecord.value }),
+ value: First(Topic.addressChoices).value
+ }
+ ],
+ actions: [
+ { type: "Action.Submit", title: "Continue", id: "Continue", data: { actionSubmitId: "Continue" } },
+ { type: "Action.Submit", title: "Add an address", id: "AddNew", data: { actionSubmitId: "AddNew" }, associatedInputs: "none" }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.selectionActionId
+ selectedAddress: Topic.selectedAddressID
+ outputType:
+ properties:
+ actionSubmitId: String
+ selectedAddress: String
+
+ # Handle cancel
+ - kind: ConditionGroup
+ id: handleSelectionCancel
+ conditions:
+ - id: selectionCancelled
+ condition: =Topic.selectionActionId = "Cancel"
+ actions:
+ - kind: SendActivity
+ id: selectionCancelMsg
+ activity: Your request has been cancelled. Is there anything else I can help you with?
+ - kind: CancelAllDialogs
+ id: selectionCancelAll
+
+ # Handle add new address
+ - kind: ConditionGroup
+ id: handleAddNewAddress
+ conditions:
+ - id: addNewSelected
+ condition: =Topic.selectionActionId = "AddNew"
+ actions:
+ - kind: SetVariable
+ id: setIsNewAddressFromSelection
+ variable: Topic.isNewAddress
+ value: =true
+
+ - kind: SetVariable
+ id: clearSelectedAddressID
+ variable: Topic.selectedAddressID
+ value: =""
+
+ # Get the selected address details (only if not adding new)
+ - kind: ConditionGroup
+ id: checkIfContinueSelected
+ conditions:
+ - id: continueSelected
+ condition: =Topic.selectionActionId = "Continue"
+ actions:
+ - kind: SetVariable
+ id: setSelectedAddress
+ displayName: Set selected address details
+ variable: Topic.selectedAddress
+ value: =LookUp(Topic.homeAddresses, AddressID = Topic.selectedAddressID)
+
+ # Set current address values for form pre-population
+ - kind: SetVariable
+ id: setCurrentAddressLine1
+ variable: Topic.currentAddressLine1
+ value: =Topic.selectedAddress.AddressLine1
+
+ - kind: SetVariable
+ id: setCurrentAddressLine2
+ variable: Topic.currentAddressLine2
+ value: =Topic.selectedAddress.AddressLine2
+
+ - kind: SetVariable
+ id: setCurrentCity
+ variable: Topic.currentCity
+ value: =Topic.selectedAddress.City
+
+ - kind: SetVariable
+ id: setCurrentStateProvince
+ variable: Topic.currentStateProvince
+ value: =Topic.selectedAddress.StateProvinceCode
+
+ - kind: SetVariable
+ id: setCurrentPostalCode
+ variable: Topic.currentPostalCode
+ value: =Topic.selectedAddress.PostalCode
+
+ - kind: SetVariable
+ id: setCurrentCountryCode
+ variable: Topic.currentCountryCode
+ value: =Topic.selectedAddress.CountryCode
+
+ - kind: SetVariable
+ id: setCurrentIsPrimary
+ variable: Topic.currentIsPrimary
+ value: =Topic.selectedAddress.IsPrimary
+
+ # Step 5: Collect new address information using Adaptive Card
+ - kind: AdaptiveCardPrompt
+ id: collectAddressCard
+ displayName: Collect new address information
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Update Your Home Address",
+ weight: "Bolder",
+ size: "Large"
+ },
+ {
+ type: "TextBlock",
+ text: "Please enter your new address details:",
+ wrap: true
+ },
+ {
+ type: "Input.Text",
+ id: "addressLine1",
+ label: "Address line 1",
+ placeholder: "Street address",
+ isRequired: true,
+ errorMessage: "Address line 1 is required.",
+ value: If(IsBlank(Topic.currentAddressLine1), "", Topic.currentAddressLine1)
+ },
+ {
+ type: "Input.Text",
+ id: "addressLine2",
+ label: "Address line 2",
+ placeholder: "Apt, Suite, Unit, etc. (optional)",
+ value: If(IsBlank(Topic.currentAddressLine2), "", Topic.currentAddressLine2)
+ },
+ {
+ type: "Input.Text",
+ id: "city",
+ label: "City",
+ placeholder: "City",
+ isRequired: true,
+ errorMessage: "City is required.",
+ value: If(IsBlank(Topic.currentCity), "", Topic.currentCity)
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "country",
+ label: "Country",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a country.",
+ value: If(IsBlank(Topic.currentCountryCode), "USA", Topic.currentCountryCode),
+ choices: [
+ { title: "United States", value: "USA" },
+ { title: "Canada", value: "CAN" },
+ { title: "United Kingdom", value: "GBR" },
+ { title: "Australia", value: "AUS" },
+ { title: "Germany", value: "DEU" },
+ { title: "France", value: "FRA" },
+ { title: "India", value: "IND" },
+ { title: "Japan", value: "JPN" },
+ { title: "Mexico", value: "MEX" },
+ { title: "Brazil", value: "BRA" }
+ ]
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "stateProvince",
+ label: "State/province",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a state/province.",
+ value: If(IsBlank(Topic.currentStateProvince), "", Topic.currentStateProvince),
+ choices: [
+ { title: "Alabama", value: "USA-AL" },
+ { title: "Alaska", value: "USA-AK" },
+ { title: "Arizona", value: "USA-AZ" },
+ { title: "Arkansas", value: "USA-AR" },
+ { title: "California", value: "USA-CA" },
+ { title: "Colorado", value: "USA-CO" },
+ { title: "Connecticut", value: "USA-CT" },
+ { title: "Delaware", value: "USA-DE" },
+ { title: "Florida", value: "USA-FL" },
+ { title: "Georgia", value: "USA-GA" },
+ { title: "Hawaii", value: "USA-HI" },
+ { title: "Idaho", value: "USA-ID" },
+ { title: "Illinois", value: "USA-IL" },
+ { title: "Indiana", value: "USA-IN" },
+ { title: "Iowa", value: "USA-IA" },
+ { title: "Kansas", value: "USA-KS" },
+ { title: "Kentucky", value: "USA-KY" },
+ { title: "Louisiana", value: "USA-LA" },
+ { title: "Maine", value: "USA-ME" },
+ { title: "Maryland", value: "USA-MD" },
+ { title: "Massachusetts", value: "USA-MA" },
+ { title: "Michigan", value: "USA-MI" },
+ { title: "Minnesota", value: "USA-MN" },
+ { title: "Mississippi", value: "USA-MS" },
+ { title: "Missouri", value: "USA-MO" },
+ { title: "Montana", value: "USA-MT" },
+ { title: "Nebraska", value: "USA-NE" },
+ { title: "Nevada", value: "USA-NV" },
+ { title: "New Hampshire", value: "USA-NH" },
+ { title: "New Jersey", value: "USA-NJ" },
+ { title: "New Mexico", value: "USA-NM" },
+ { title: "New York", value: "USA-NY" },
+ { title: "North Carolina", value: "USA-NC" },
+ { title: "North Dakota", value: "USA-ND" },
+ { title: "Ohio", value: "USA-OH" },
+ { title: "Oklahoma", value: "USA-OK" },
+ { title: "Oregon", value: "USA-OR" },
+ { title: "Pennsylvania", value: "USA-PA" },
+ { title: "Rhode Island", value: "USA-RI" },
+ { title: "South Carolina", value: "USA-SC" },
+ { title: "South Dakota", value: "USA-SD" },
+ { title: "Tennessee", value: "USA-TN" },
+ { title: "Texas", value: "USA-TX" },
+ { title: "Utah", value: "USA-UT" },
+ { title: "Vermont", value: "USA-VT" },
+ { title: "Virginia", value: "USA-VA" },
+ { title: "Washington", value: "USA-WA" },
+ { title: "West Virginia", value: "USA-WV" },
+ { title: "Wisconsin", value: "USA-WI" },
+ { title: "Wyoming", value: "USA-WY" },
+ { title: "District of Columbia", value: "USA-DC" },
+ { title: "Ontario", value: "CAN-ON" },
+ { title: "Quebec", value: "CAN-QC" },
+ { title: "British Columbia", value: "CAN-BC" },
+ { title: "Alberta", value: "CAN-AB" }
+ ]
+ },
+ {
+ type: "Input.Text",
+ id: "postalCode",
+ label: "Postal/ZIP code",
+ placeholder: "Postal code",
+ isRequired: true,
+ errorMessage: "Postal code is required.",
+ value: If(IsBlank(Topic.currentPostalCode), "", Topic.currentPostalCode)
+ },
+ {
+ type: "Input.Toggle",
+ id: "isPrimary",
+ title: "Set as primary address",
+ value: If(Topic.currentIsPrimary = true, "true", "false"),
+ valueOn: "true",
+ valueOff: "false"
+ }
+ ],
+ actions: [
+ {
+ type: "Action.Submit",
+ title: "Update Address",
+ data: {
+ action: "updateAddress"
+ }
+ },
+ {
+ type: "Action.Submit",
+ title: "Cancel",
+ data: {
+ action: "cancel"
+ }
+ }
+ ]
+ }
+ output:
+ binding:
+ addressLine1: Topic.newAddressLine1
+ addressLine2: Topic.newAddressLine2
+ city: Topic.newCity
+ stateProvince: Topic.newStateProvince
+ country: Topic.newCountry
+ postalCode: Topic.newPostalCode
+ isPrimary: Topic.newIsPrimary
+ action: Topic.cardAction
+ outputType:
+ properties:
+ addressLine1: String
+ addressLine2: String
+ city: String
+ stateProvince: String
+ country: String
+ postalCode: String
+ isPrimary: String
+ action: String
+
+ # Step 6: Check if user cancelled
+ - kind: ConditionGroup
+ id: checkCancelAction
+ conditions:
+ - id: cancelCondition
+ condition: =Topic.cardAction = "cancel"
+ actions:
+ - kind: SendActivity
+ id: sendCancelMessage
+ activity: Address update cancelled. No changes were made.
+
+ - kind: EndDialog
+ id: endOnCancel
+
+ # Step 7: Validate required fields
+ - kind: ConditionGroup
+ id: validateFields
+ conditions:
+ - id: missingFieldsCondition
+ condition: =IsBlank(Topic.newAddressLine1) || IsBlank(Topic.newCity) || IsBlank(Topic.newStateProvince) || IsBlank(Topic.newPostalCode)
+ actions:
+ - kind: SendActivity
+ id: sendValidationError
+ activity: Please fill in all required fields (Address Line 1, City, State/Province, and Postal Code).
+
+ - kind: EndDialog
+ id: endOnValidationError
+
+ # Step 8: Call Add or Update API based on isNewAddress flag
+ - kind: ConditionGroup
+ id: checkAddOrUpdate
+ conditions:
+ - id: isNewAddressCondition
+ condition: =Topic.isNewAddress = true
+ displayName: Add new address
+ actions:
+ # Call Add API (no Address_ID parameter)
+ - kind: BeginDialog
+ id: addAddress_BeginDialog
+ displayName: Add New Residential Address
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{Event_Effective_Date}"",""value"":"""& Text(Now(), "yyyy-MM-dd") &"""},{""key"":""{Country_Code}"",""value"":""" & Topic.newCountry & """},{""key"":""{State_Province_Code}"",""value"":""" & Topic.newStateProvince & """},{""key"":""{Address_Line_1}"",""value"":""" & Topic.newAddressLine1 & """},{""key"":""{Address_Line_2}"",""value"":""" & If(IsBlank(Topic.newAddressLine2), "", Topic.newAddressLine2) & """},{""key"":""{City}"",""value"":""" & Topic.newCity & """},{""key"":""{Postal_Code}"",""value"":""" & Topic.newPostalCode & """},{""key"":""{Is_Primary}"",""value"":""" & If(Topic.newIsPrimary = "true", "true", "false") & """}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeAddResidentialAddress
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.updateErrorResponse
+ isSuccess: Topic.updateIsSuccess
+ workdayResponse: Topic.updateWorkdayResponse
+
+ elseActions:
+ # Call Update API (with Address_ID parameter)
+ - kind: BeginDialog
+ id: updateAddress_BeginDialog
+ displayName: Update Residential Address
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{Event_Effective_Date}"",""value"":"""& Text(Now(), "yyyy-MM-dd") &"""},{""key"":""{Address_ID}"",""value"":""" & Topic.selectedAddressID & """},{""key"":""{Country_Code}"",""value"":""" & Topic.newCountry & """},{""key"":""{State_Province_Code}"",""value"":""" & Topic.newStateProvince & """},{""key"":""{Address_Line_1}"",""value"":""" & Topic.newAddressLine1 & """},{""key"":""{Address_Line_2}"",""value"":""" & If(IsBlank(Topic.newAddressLine2), "", Topic.newAddressLine2) & """},{""key"":""{City}"",""value"":""" & Topic.newCity & """},{""key"":""{Postal_Code}"",""value"":""" & Topic.newPostalCode & """},{""key"":""{Is_Primary}"",""value"":""" & If(Topic.newIsPrimary = "true", "true", "false") & """}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeUpdateResidentialAddress
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.updateErrorResponse
+ isSuccess: Topic.updateIsSuccess
+ workdayResponse: Topic.updateWorkdayResponse
+
+ # Step 9: Show result
+ - kind: ConditionGroup
+ id: checkUpdateSuccess
+ conditions:
+ - id: updateSuccessCondition
+ condition: =Topic.updateIsSuccess = true
+ actions:
+ - kind: SendActivity
+ id: sendSuccessIntroMessage
+ activity: Great! You've updated your address.
+
+ - kind: SendActivity
+ id: sendSuccessMessage
+ activity:
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "✅ Address updated",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "TextBlock",
+ text: Topic.newAddressLine1 & ", " & Topic.newCity & ", " & Topic.newStateProvince & " " & Topic.newPostalCode & " (" & If(Topic.newIsPrimary = "true", "Primary", "Home") & ")",
+ wrap: true,
+ spacing: "Small"
+ }
+ ]
+ }
+
+ - kind: CancelAllDialogs
+ id: endOnSuccess
+
+ elseActions:
+ - kind: SendActivity
+ id: sendUpdateErrorMessage
+ activity: |-
+ There was an error updating your address. Please try again later or contact support.
+
+ **Error details:** {Topic.updateErrorResponse}
+
+ - kind: CancelAllDialogs
+ id: endOnUpdateError
+
+inputType: {}
+outputType:
+ properties:
+ updateIsSuccess:
+ displayName: Update Success
+ type: Boolean
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/update_address.png b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/update_address.png
new file mode 100644
index 00000000..690daa35
Binary files /dev/null and b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/EmployeeUpdateResidentialAddress/update_address.png differ
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/README.md b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/README.md
new file mode 100644
index 00000000..4f751033
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/README.md
@@ -0,0 +1,16 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Employee Scenarios
+
+Copilot Studio topics for employee self-service actions in Workday.
+
+| Folder | Description |
+| --- | --- |
+| [EmployeeAddDependents/](./EmployeeAddDependents/) | Add dependents to benefits |
+| [EmployeeGetVacationBalance/](./EmployeeGetVacationBalance/) | Check vacation balance |
+| [EmployeeUpdateResidentialAddress/](./EmployeeUpdateResidentialAddress/) | Update residential address |
+| [WorkdayEmployeeRequestTimeOff/](./WorkdayEmployeeRequestTimeOff/) | Request time off |
+| [WorkdayGetUserProfile/](./WorkdayGetUserProfile/) | View user profile |
+| [WorkdayManageEmergencyContact/](./WorkdayManageEmergencyContact/) | Manage emergency contacts |
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/README.md b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/README.md
new file mode 100644
index 00000000..c0fec5fa
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/README.md
@@ -0,0 +1,229 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Employee Request Time Off
+
+This topic lets employees submit single-day or multi-day time off requests to Workday directly from a Copilot Studio agent.
+
+When an employee triggers this topic, the agent:
+
+1. Fetches current leave balances from Workday
+2. Shows an Adaptive Card form with leave type, dates, hours, and reason
+3. Submits the request to the Workday `Enter_Time_Off` API
+4. Displays a confirmation card or a friendly error with retry options
+
+**Example trigger phrases:** "Request time off" · "Request vacation from January 5th to January 10th" · "Submit sick leave for next week"
+
+
+
+
+{: .important }
+> This topic ships with sample values from a reference Workday tenant. Every Workday tenant is configured differently — you must replace the Plan IDs, Reason IDs, leave types, and tenant URL with values from your own Workday setup before importing. See [Configure the topic](#configure-the-topic) for details.
+
+## Prerequisites
+
+Before you start, make sure you have:
+
+- The **msdyn_copilotforemployeeselfservicehr** managed solution installed in your agent
+- A Workday tenant with the **Absence_Management** module enabled
+- A Workday connector configured with **User**-level authentication in your Power Platform environment
+- The global variable **`Global.ESS_UserContext_Employee_Id`** populated at session start with the logged-in employee's Workday Employee ID
+- The **`msdyn_HRWorkdayHCMEmployeeGetVacationBalance`** template already imported (from the [EmployeeGetVacationBalance](../EmployeeGetVacationBalance/) folder)
+- Your organization's Workday Absence Plan IDs, Time Off Type IDs, and Reason IDs
+
+## What's in this folder
+
+| File | Description | Do you need to edit it? |
+|------|-------------|------------------------|
+| `topic.yaml` | Conversation flow, Adaptive Card form, configuration tables, error handling | Yes — update tenant URL, Plan IDs, Reason IDs |
+| `msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay.xml` | SOAP template for the Workday `Enter_Time_Off` API | Usually no |
+| `requestTimeOffAdapativeCard.png` | Screenshot of the Adaptive Card form | No |
+| `requestTimeOffSubmitted.png` | Screenshot of the confirmation card | No |
+| `README.md` | This file | No |
+
+## Import the templates
+
+Before configuring the topic, import the XML templates into your agent.
+
+1. Add `msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay.xml` to your agent's ESS Template Configuration.
+
+2. If you haven't already, import the balance template `msdyn_HRWorkdayHCMEmployeeGetVacationBalance` from the [EmployeeGetVacationBalance](../EmployeeGetVacationBalance/) folder. This topic calls it to display leave balances.
+
+## Configure the topic
+
+Open `topic.yaml` and update the following values to match your Workday tenant. All sample values shown below come from a reference tenant and **must** be replaced.
+
+### Step 1: Set your Workday tenant URL
+
+Find the `set_workday_url` node and replace `` with your tenant name:
+
+```yaml
+# Before
+value: https://impl.workday.com//home.htmld
+
+# After — replace contoso_corp with your tenant
+value: https://impl.workday.com/contoso_corp/home.htmld
+```
+
+Your Workday URL typically follows the pattern `https://.workday.com//home.htmld`. Check your browser address bar when logged into Workday.
+
+### Step 2: Update Plan IDs
+
+Find the `set_plan_config` node. Replace each `PlanID` with the corresponding ID from your Workday tenant:
+
+```
+=Table(
+ {PlanID: "ABSENCE_PLAN-6-139", DisplayName: "Floating holiday"},
+ {PlanID: "ABSENCE_PLAN-6-159", DisplayName: "Sick leave"},
+ {PlanID: "ABSENCE_PLAN-6-158", DisplayName: "Vacation"}
+)
+```
+
+You can find your Plan IDs in Workday under **Setup > Absence Plans > Plan ID**. Only plans listed here **and** returned by the Workday API will appear in the balance header.
+
+### Step 3: Update Reason IDs
+
+Find the **second** `set_time_off_reason_config` node. Replace each `ReasonID` with your tenant's value:
+
+| ReasonKey | TimeOffTypeID | ReasonID ← replace this |
+|-----------|---------------|------------------------|
+| `full_day` | `ESS_Vacation_Hours` | `Full Day_Vac` |
+| `half_day_am` | `ESS_Vacation_Hours` | `Half Day_AM_Vac` |
+| `half_day_pm` | `ESS_Vacation_Hours` | `Half Day_PM_Vac` |
+| `full_day` | `ESS_Floating_Holiday_Hours` | `full_day_floating` |
+| `half_day_am` | `ESS_Floating_Holiday_Hours` | `half_day_am_floating` |
+| `half_day_pm` | `ESS_Floating_Holiday_Hours` | `half_day_pm_floating` |
+| `full_day` | `ESS_Comp_Off` | `Full day-COI` |
+| `half_day_am` | `ESS_Comp_Off` | `Half day AM-COI` |
+| `half_day_pm` | `ESS_Comp_Off` | `Half day PM-COI` |
+| `full_day` | `ESS_Sick_Time_Off` | `Full day_Sick_IN` |
+| `half_day_am` | `ESS_Sick_Time_Off` | `Half day AM_Sick_IN` |
+| `half_day_pm` | `ESS_Sick_Time_Off` | `Half day PM_Sick_IN` |
+
+Don't rename the `ReasonKey` values — they're bound to the Adaptive Card "Reason for time off" dropdown.
+
+### Step 4 (optional): Update leave-type choices
+
+If your organization uses different leave types than Vacation, Floating holiday, Sick leave, and Comp off, update two places:
+
+**The Adaptive Card dropdown** — in the `collect_time_off` node, edit the `choices` array. For example, to add Bereavement:
+
+```json
+{ "title": "Bereavement", "value": "ESS_Bereavement" }
+```
+
+**The keyword mapping** — in the `set_leave_type_config` node, add a row so the agent can pre-select the type from the user's utterance:
+
+| Keywords (comma-separated) | LeaveTypeValue |
+|---------------------------|----------------|
+| `vacation,annual,pto,holiday pay` | `Vacation_Hours` |
+| `floating,floater,float day` | `Floating_Holiday_Hours` |
+| `sick,illness,medical,unwell,not feeling well` | `ESS_Sick_Time_Off` |
+
+If you add a new leave type, also add matching rows to `TimeOffReasonConfig` (one per ReasonKey).
+
+### Step 5 (optional): Update the Workday icon
+
+Find the `set_workday_icon_url` node and replace the 1×1 pixel placeholder with your organization's Workday icon URL.
+
+## Import and test the topic
+
+1. In Copilot Studio, go to **Topics > + Add a topic > From file**.
+2. Select `topic.yaml` and import.
+3. Open **Test your agent** and try these:
+
+| What to test | What to expect |
+|-------------|---------------|
+| Say "I want to request time off" | Balance header appears, then the Adaptive Card form |
+| Say "Request vacation from September 15 to September 19" and submit | Success card with date range, type, hours, and reason |
+| Submit with end date before start date | Error: "The end date can't be earlier than the start date" with retry |
+| Select "I don't see my leave type listed" and submit | Redirect message with a link to Workday |
+| Click **Cancel** | "Leave request was cancelled - nothing was submitted" |
+| Submit dates that overlap an existing request | Friendly AI-rewritten error with retry |
+
+If balances show as empty but the form appears, your `PlanConfig` Plan IDs likely don't match your Workday tenant.
+
+## XML template reference
+
+The `msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay.xml` file is a SOAP template for the Workday `Enter_Time_Off_Request` operation. You usually don't need to edit it — placeholder tokens like `{Employee_ID}` and `{timeoff_Dates}` are filled at runtime.
+
+Two settings you might want to change:
+
+| Setting | Default | Change if… |
+|---------|---------|------------|
+| `` | `false` (requires manager approval) | Your process skips approval — set to `true` |
+| `` and `wd:version` | `v42.0` | Your tenant uses a different API version |
+
+{: .important }
+> If you change the API version, update it in **both** the `` element and the `wd:version` attribute. Mismatched versions cause API errors.
+
+## What you can safely edit in the YAML
+
+| What | Where in `topic.yaml` |
+|------|----------------------|
+| Workday tenant URL | `set_workday_url` node |
+| Plan IDs | `set_plan_config` node |
+| Reason IDs | Second `set_time_off_reason_config` node |
+| Leave-type keywords | `set_leave_type_config` node |
+| Adaptive Card dropdown choices | `collect_time_off` node → `choices` array |
+| Trigger phrases | `modelDescription` block |
+| Workday icon | `set_workday_icon_url` node |
+| Balance effective date | `set_as_of_effective_date` node (defaults to `Today()`) |
+
+**Don't** change these — they'll break the topic:
+
+- Node `id` values (`id: main`, `id: collect_time_off`, etc.) — referenced by `GotoAction` jumps
+- `kind:` values — Copilot Studio node types
+- `dialog:` references — point to the shared solution
+- `output: binding:` mappings — must match shared dialog outputs
+- XML placeholder tokens (`{Employee_ID}`, `{timeoff_Dates}`, etc.) — filled at runtime
+- The `EmployeeName` input — the model uses it to validate the request is for the logged-in user
+
+## Dependencies
+
+This topic depends on three external components:
+
+- **Workday `Get_Time_Off_Plan_Balances`** — Absence_Management service, User-level auth. Uses the `msdyn_HRWorkdayHCMEmployeeGetVacationBalance` template from [EmployeeGetVacationBalance](../EmployeeGetVacationBalance/). Called at topic start to show balances.
+
+- **Workday `Enter_Time_Off`** — Absence_Management v42.0, User-level auth. Uses the `msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay` template in this folder. Called after form submission.
+
+- **`WorkdaySystemGetCommonExecution` dialog** — from the `msdyn_copilotforemployeeselfservicehr` solution. Executes XML templates against the Workday connector. Must be pre-installed.
+
+## Troubleshooting
+
+**Balance header is empty or shows all zeros**
+Your `PlanConfig` Plan IDs don't match your Workday tenant, or the balance template isn't imported. Verify the IDs and confirm `msdyn_HRWorkdayHCMEmployeeGetVacationBalance` is in your ESS Template Configuration.
+
+**"Something went wrong" when submitting**
+The `ReasonID` values in `TimeOffReasonConfig` don't match your Workday tenant. Check your IDs in Workday under **Setup > Time Off Reasons**.
+
+**Authentication error**
+`Global.ESS_UserContext_Employee_Id` is empty or the Workday connector isn't configured. Verify both.
+
+**Vacation isn't pre-selected when the user says "request vacation"**
+The keyword mapping uses `Vacation_Hours` but the dropdown value is `ESS_Vacation_Hours`. Update `LeaveTypeConfig` to use `ESS_Vacation_Hours`. See [Open Questions](#open-questions).
+
+**Success card shows a raw ID like `ESS_Vacation_Hours`**
+The `Switch()` expression in the success card doesn't match the dropdown values. See [Open Questions](#open-questions).
+
+## Tips
+
+- Import the balance template **before** this topic — the balance lookup runs at topic start and fails silently if the template is missing.
+- Update `PlanConfig`, `TimeOffReasonConfig`, **and** the Adaptive Card `choices` together — they're interconnected but maintained separately.
+- Test with a real Workday employee ID that has leave balances.
+- Keep `ReasonKey` values as `full_day`, `half_day_am`, `half_day_pm` — they're bound to the Adaptive Card dropdown.
+
+## Handle repo updates
+
+When this repo publishes a new version of the topic:
+
+1. **Diff first.** Your customized tenant URL, Plan IDs, Reason IDs, and leave-type keywords will be overwritten if you re-import `topic.yaml`.
+2. **Merge.** Copy your config values from the current topic, pull the updated file, paste your values back, then import.
+3. The XML template is safe to overwrite — unless you changed `Auto_Complete` or the API version.
+
+## Known limitations
+
+- **Leave type pre-selection may not work for Vacation and Floating Holiday.** The keyword mapping (`LeaveTypeConfig`) uses `Vacation_Hours` and `Floating_Holiday_Hours`, but the Adaptive Card dropdown expects `ESS_Vacation_Hours` and `ESS_Floating_Holiday_Hours`. To fix this, update the `LeaveTypeValue` entries in `LeaveTypeConfig` to include the `ESS_` prefix.
+
+- **Success card may show raw IDs instead of friendly names.** After a successful submission, the confirmation card may display `ESS_Vacation_Hours` instead of "Vacation". To work around this, update the `Switch()` expression in the success card to match the `ESS_`-prefixed values from the dropdown.
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay.xml
new file mode 100644
index 00000000..8d63c0c0
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay.xml
@@ -0,0 +1,56 @@
+
+
+
+
+ User
+
+ msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay
+ Absence_Management
+ v42.0
+
+
+
+
+ //*[local-name()='Time_Off_Event_Reference']/*[local-name()='ID' and @*[local-name()='type']='WID']
+ Event_WID
+
+
+ //*[local-name()='Time_Off_Entry_Reference']
+ Entry_References
+
+
+
+
+
+
+
+
+
+ false
+ true
+ true
+
+ {timeoff_Comment}
+
+
+
+
+ {Employee_ID}
+
+
+
+ {timeoff_Date}
+ {timeoff_Hours_Per_Day}
+
+ {timeoff_Time_Off_Type}
+
+
+ {timeoff_Reason}
+
+ {timeoff_Comment}
+
+
+
+
+
+
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/requestTimeOffAdapativeCard.png b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/requestTimeOffAdapativeCard.png
new file mode 100644
index 00000000..649b781b
Binary files /dev/null and b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/requestTimeOffAdapativeCard.png differ
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/requestTimeOffSubmitted.png b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/requestTimeOffSubmitted.png
new file mode 100644
index 00000000..5b41ff1e
Binary files /dev/null and b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/requestTimeOffSubmitted.png differ
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/request_time_off.png b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/request_time_off.png
new file mode 100644
index 00000000..e2577532
Binary files /dev/null and b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/request_time_off.png differ
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/topic.yaml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/topic.yaml
new file mode 100644
index 00000000..d2ecddda
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/topic.yaml
@@ -0,0 +1,907 @@
+kind: AdaptiveDialog
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: EmployeeName
+ description: The name of the employee to fetch data for.
+ entity: PersonNamePrebuiltEntity
+ shouldPromptUser: false
+
+ - kind: AutomaticTaskInput
+ propertyName: InputStartDate
+ description: The start date of the time off request.
+ entity: DateTimePrebuiltEntity
+ shouldPromptUser: false
+
+ - kind: AutomaticTaskInput
+ propertyName: InputEndDate
+ description: The end date of the time off request.
+ entity: DateTimePrebuiltEntity
+ shouldPromptUser: false
+
+ - kind: AutomaticTaskInput
+ propertyName: InputHoursPerDay
+ description: The number of hours per day for the time off request.
+ entity: NumberPrebuiltEntity
+ shouldPromptUser: false
+
+ - kind: AutomaticTaskInput
+ propertyName: InputTimeOffType
+ description: Extract the type of leave mentioned such as vacation, sick, sick leave, floating holiday, floater, PTO, annual leave, medical, or illness. Extract keywords like 'sick', 'vacation', 'floating', 'PTO', 'annual'.
+ entity: StringPrebuiltEntity
+ shouldPromptUser: false
+
+ - kind: AutomaticTaskInput
+ propertyName: InputLeaveReason
+ description: The reason or comment for the time off request. Extract any reason, justification, or explanation the user provides for their leave such as 'family event', 'doctor appointment', 'personal', 'travel', etc.
+ entity: StringPrebuiltEntity
+ shouldPromptUser: false
+
+modelDescription: |-
+ You will respond only to requests related to requesting time off for the user making the request.
+ All time off requests are submitted to Workday (Absence_Management) and pertain exclusively to the requesting user.
+ This topic may prompt the user for input (time off type, start date, end date, reason, and hours) if they are requesting time off for themselves.
+ This data exclusively belongs to the user making the request. Do not respond to questions about other people's data.
+
+ Example invalid requests:
+ "Request time off for my manager"
+ "Request time off for my sister"
+ "Request time off for [EmployeeName]"
+
+ Example valid requests:
+ "Request time off"
+ "I need to submit time off"
+ "Please request vacation from January 5th to January 10th"
+ "Request sick leave for next week"
+ "I need time off from 2025-09-15 to 2025-09-20 for a family event"
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent: {}
+ actions:
+ - kind: SetVariable
+ id: set_workday_url
+ variable: Topic.WorkdayUrl
+ value: https://impl.workday.com//home.htmld
+
+ - kind: SetVariable
+ id: set_workday_icon_url
+ variable: Topic.WorkdayIconUrl
+ value: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=
+
+ # ================================================================
+ # DATE CONFIGURATION
+ # Set the effective date for balance lookup. Default is Today().
+ # Customers can change this to a specific date if needed.
+ # ================================================================
+ - kind: SetVariable
+ id: set_as_of_effective_date
+ variable: Topic.AsOfEffectiveDate
+ value: =Today()
+
+ # ================================================================
+ # REASON MAPPING CONFIGURATION
+ # Maps generic reason (Full Day / Half Day AM / Half Day PM) +
+ # Time Off Type to the correct Workday ReasonID.
+ # ReasonKey must match the dropdown values below.
+ # TimeOffTypeID must match the time off type dropdown values.
+ # ================================================================
+ - kind: SetVariable
+ id: set_time_off_reason_config
+ variable: Topic.TimeOffReasonConfig
+ value: |-
+ =Table(
+ {ReasonKey: "full_day", TimeOffTypeID: "ESS_Floating_Holiday_Hours", ReasonID: "full_day_floating"},
+ {ReasonKey: "half_day_am", TimeOffTypeID: "ESS_Floating_Holiday_Hours", ReasonID: "half_day_am_floating"},
+ {ReasonKey: "half_day_pm", TimeOffTypeID: "ESS_Floating_Holiday_Hours", ReasonID: "half_day_pm_floating"},
+ {ReasonKey: "full_day", TimeOffTypeID: "ESS_Comp_Off", ReasonID: "Full day-COI"},
+ {ReasonKey: "half_day_am", TimeOffTypeID: "ESS_Comp_Off", ReasonID: "Half day AM-COI"},
+ {ReasonKey: "half_day_pm", TimeOffTypeID: "ESS_Comp_Off", ReasonID: "Half day PM-COI"},
+ {ReasonKey: "full_day", TimeOffTypeID: "ESS_Sick_Time_Off", ReasonID: "Full day_Sick_IN"},
+ {ReasonKey: "half_day_am", TimeOffTypeID: "ESS_Sick_Time_Off", ReasonID: "Half day AM_Sick_IN"},
+ {ReasonKey: "half_day_pm", TimeOffTypeID: "ESS_Sick_Time_Off", ReasonID: "Half day PM_Sick_IN"},
+ {ReasonKey: "full_day", TimeOffTypeID: "ESS_Vacation_Hours", ReasonID: "Full Day_Vac"},
+ {ReasonKey: "half_day_am", TimeOffTypeID: "ESS_Vacation_Hours", ReasonID: "Half Day_AM_Vac"},
+ {ReasonKey: "half_day_pm", TimeOffTypeID: "ESS_Vacation_Hours", ReasonID: "Half Day_PM_Vac"}
+ )
+
+ # ================================================================
+ # LEAVE TYPE CONFIGURATION
+ # Edit keywords below to customize how user phrases map to leave types.
+ # Keywords are comma-separated and case-insensitive.
+ # LeaveTypeValue must match the dropdown choice values.
+ # ================================================================
+ - kind: SetVariable
+ id: set_leave_type_config
+ variable: Topic.LeaveTypeConfig
+ value: |-
+ =Table(
+ {Keywords: "vacation,annual,pto,holiday pay", LeaveTypeValue: "ESS_Vacation_Hours"},
+ {Keywords: "floating,floater,float day", LeaveTypeValue: "ESS_Floating_Holiday_Hours"},
+ {Keywords: "sick,illness,medical,unwell,not feeling well", LeaveTypeValue: "ESS_Sick_Time_Off"}
+ )
+
+ # Map extracted time off type to dropdown value using config table
+ - kind: SetVariable
+ id: set_mapped_time_off_type
+ variable: Topic.MappedTimeOffType
+ value: |-
+ =If(
+ IsBlank(Topic.InputTimeOffType),
+ "",
+ Coalesce(
+ First(
+ Filter(
+ Topic.LeaveTypeConfig,
+ !IsEmpty(Filter(Split(Keywords, ","), Trim(Value) in Lower(Topic.InputTimeOffType)))
+ )
+ ).LeaveTypeValue,
+ ""
+ )
+ )
+
+ - kind: BeginDialog
+ id: get_leave_balance
+ displayName: Get Leave Balance
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":""" & Text(Topic.AsOfEffectiveDate, "yyyy-MM-dd") & """}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetVacationBalance
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.balanceErrorResponse
+ isSuccess: Topic.balanceIsSuccess
+ workdayResponse: Topic.balanceResponse
+
+ - kind: ParseValue
+ id: parse_balance
+ variable: Topic.parsedBalance
+ valueType:
+ kind: Record
+ properties:
+ PlanDescriptor:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ PlanID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ RemainingBalance:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ UnitOfTime:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.balanceResponse
+
+ # ================================================================
+ # PLAN BALANCE CONFIGURATION
+ # Maps Plan IDs (from balance API) to display names.
+ # Update PlanID values to match your Workday configuration.
+ # Only plans listed here AND returned by the API will be displayed.
+ # ================================================================
+ - kind: SetVariable
+ id: set_plan_config
+ variable: Topic.PlanConfig
+ value: |-
+ =Table(
+ {PlanID: "ABSENCE_PLAN-6-139", DisplayName: "Floating holiday"},
+ {PlanID: "ABSENCE_PLAN-6-159", DisplayName: "Sick leave"},
+ {PlanID: "ABSENCE_PLAN-6-158", DisplayName: "Vacation"}
+ )
+
+ # Create merged balance table for easy lookup by PlanID
+ - kind: SetVariable
+ id: set_balance_table
+ variable: Topic.BalanceTable
+ value: |-
+ =ForAll(
+ Sequence(CountRows(Topic.parsedBalance.PlanID)),
+ {
+ PlanID: Index(Topic.parsedBalance.PlanID, Value).Value,
+ Balance: Index(Topic.parsedBalance.RemainingBalance, Value).Value
+ }
+ )
+
+ # Build display data: only include plans that exist in BOTH config AND API response
+ - kind: SetVariable
+ id: set_display_balances
+ variable: Topic.DisplayBalances
+ value: |-
+ =ForAll(
+ Filter(
+ Topic.PlanConfig,
+ !IsBlank(LookUp(Topic.BalanceTable, PlanID = ThisRecord.PlanID).Balance)
+ ) As plan,
+ {
+ PlanID: plan.PlanID,
+ DisplayName: plan.DisplayName,
+ Balance: LookUp(Topic.BalanceTable, PlanID = plan.PlanID).Balance
+ }
+ )
+
+ - kind: SetVariable
+ id: build_intro_message
+ variable: Topic.introMessage
+ value: ="Sure, I'll help you submit a time off request. Here's a form where you can choose the type of leave and dates you want off. Don't see the type of leave you want? [Book directly in Workday](" & Topic.WorkdayUrl & ")"
+
+ - kind: SendActivity
+ id: intro_message
+ activity: "{Topic.introMessage}"
+
+ - kind: ConditionGroup
+ id: need_inputs
+ conditions:
+ - id: always_true
+ condition: true
+ actions:
+ - kind: AdaptiveCardPrompt
+ id: collect_time_off
+ displayName: Ask time off type, start date, end date, reason and hours
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Book your time off",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ color: "Default"
+ },
+ {
+ type: "Container",
+ style: "emphasis",
+ bleed: true,
+ items: [
+ {
+ type: "TextBlock",
+ text: "Available balance in hours as of " & Text(Topic.AsOfEffectiveDate, "mmmm d, yyyy"),
+ size: "Small",
+ weight: "Bolder",
+ wrap: true
+ },
+ {
+ type: "ColumnSet",
+ columns: ForAll(
+ Topic.DisplayBalances,
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ { type: "TextBlock", text: DisplayName, size: "Small", wrap: true },
+ { type: "TextBlock", text: Text(Balance), weight: "Bolder", spacing: "Small" }
+ ]
+ }
+ )
+ }
+ ]
+ },
+ {
+ type: "TextBlock",
+ text: "Fields marked with * are required.",
+ wrap: true,
+ spacing: "Medium",
+ size: "Small"
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "timeOffType",
+ label: "Type of time off",
+ style: "compact",
+ value: Topic.MappedTimeOffType,
+ isRequired: true,
+ errorMessage: "Please select a type.",
+ choices: [
+ { title: "Vacation", value: "ESS_Vacation_Hours" },
+ { title: "Floating holiday", value: "ESS_Floating_Holiday_Hours" },
+ { title: "Sick leave", value: "ESS_Sick_Time_Off" },
+ { title: "Comp off", value: "ESS_Comp_Off" },
+ { title: "I don't see my leave type listed", value: "NOT_LISTED" }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Date",
+ id: "startDate",
+ label: "Start date",
+ value: If(IsBlank(Topic.InputStartDate), "", Text(Topic.InputStartDate, "yyyy-MM-dd")),
+ isRequired: true,
+ errorMessage: "Please select a start date."
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Date",
+ id: "endDate",
+ label: "End date",
+ value: If(IsBlank(Topic.InputEndDate), "", Text(Topic.InputEndDate, "yyyy-MM-dd")),
+ isRequired: true,
+ errorMessage: "Please select an end date."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "Input.Number",
+ id: "hoursPerDay",
+ label: "Hours per day",
+ min: 1,
+ max: 24,
+ value: If(IsBlank(Topic.InputHoursPerDay), 8, Value(Topic.InputHoursPerDay)),
+ isRequired: true,
+ errorMessage: "Please enter hours per day."
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "timeOffReason",
+ label: "Reason for time off",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a reason.",
+ choices: [
+ { title: "Full Day", value: "full_day" },
+ { title: "Half Day AM", value: "half_day_am" },
+ { title: "Half Day PM", value: "half_day_pm" }
+ ]
+ },
+ {
+ type: "Input.Text",
+ id: "leaveComment",
+ label: "Additional comments (optional)",
+ placeholder: "e.g. Details about your time off",
+ value: If(IsBlank(Topic.InputLeaveReason), "", Topic.InputLeaveReason),
+ isMultiline: true,
+ isRequired: false
+ },
+ {
+ type: "ColumnSet",
+ spacing: "Medium",
+ columns: [
+ {
+ type: "Column",
+ width: "auto",
+ items: [
+ {
+ type: "ActionSet",
+ actions: [
+ { type: "Action.Submit", title: "Submit", id: "Submit", data: { actionSubmitId: "Submit" } },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", data: { actionSubmitId: "Cancel" }, associatedInputs: "none" }
+ ]
+ }
+ ],
+ verticalContentAlignment: "Center"
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [],
+ verticalContentAlignment: "Center"
+ },
+ {
+ type: "Column",
+ width: "auto",
+ items: [
+ {
+ type: "ColumnSet",
+ spacing: "None",
+ columns: [
+ {
+ type: "Column",
+ width: "auto",
+ items: [
+ {
+ type: "Image",
+ url: Topic.WorkdayIconUrl,
+ style: "RoundedCorners",
+ size: "Small",
+ height: "20px",
+ width: "20px"
+ }
+ ],
+ verticalContentAlignment: "Center"
+ },
+ {
+ type: "Column",
+ width: "auto",
+ items: [
+ {
+ type: "TextBlock",
+ text: "Workday",
+ size: "Small",
+ color: "#242424",
+ weight: "Bolder"
+ }
+ ],
+ verticalContentAlignment: "Center",
+ spacing: "Small"
+ }
+ ]
+ }
+ ],
+ verticalContentAlignment: "Center"
+ }
+ ]
+ }
+ ],
+ actions: []
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.actionSubmitId
+ endDate: Topic.endDate
+ hoursPerDay: Topic.hoursPerDay
+ leaveComment: Topic.leaveComment
+ startDate: Topic.startDate
+ timeOffReason: Topic.timeOffReason
+ timeOffType: Topic.timeOffType
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ endDate: String
+ hoursPerDay: String
+ leaveComment: String
+ startDate: String
+ timeOffReason: String
+ timeOffType: String
+
+ - kind: ConditionGroup
+ id: handle_cancel
+ conditions:
+ - id: cancel_pressed
+ condition: =Topic.actionSubmitId = "Cancel"
+ actions:
+ - kind: SendActivity
+ id: cancel_processing_msg
+ activity: Processing cancellation...
+
+ - kind: SendActivity
+ id: cancel_ack_msg
+ activity: Leave request was cancelled - nothing was submitted.
+
+ - kind: SendActivity
+ id: cancel_followup_msg
+ activity: No worries, the form has been discarded and nothing was submitted to Workday. Want to start a new request or need anything else?
+
+ - kind: EndDialog
+ id: end_on_cancel
+
+ # Handle "I don't see my leave type listed" selection
+ - kind: ConditionGroup
+ id: handle_not_listed
+ conditions:
+ - id: type_not_listed
+ condition: =Topic.timeOffType = "NOT_LISTED"
+ actions:
+ - kind: SetVariable
+ id: set_not_listed_message
+ variable: Topic.notListedMessage
+ value: ="Since your leave type isn't listed here, you can book directly in [Workday](" & Topic.WorkdayUrl & "), or let me know if you want to change your type."
+
+ - kind: SendActivity
+ id: not_listed_msg
+ activity: "{Topic.notListedMessage}"
+
+ - kind: CancelAllDialogs
+ id: end_not_listed
+
+ # Validate end date >= start date
+ - kind: ConditionGroup
+ id: validate_dates
+ conditions:
+ - id: end_before_start
+ condition: =!IsBlank(Topic.endDate) && !IsBlank(Topic.startDate) && DateValue(Topic.endDate) < DateValue(Topic.startDate)
+ actions:
+ - kind: SendActivity
+ id: date_error_msg
+ activity: The end date can't be earlier than the start date.
+
+ # Ask user if they want to try again
+ - kind: Question
+ id: ask_retry_dates
+ interruptionPolicy:
+ allowInterruption: false
+
+ variable: Topic.retryDateChoice
+ prompt: Would you like to try with different dates?
+ entity: BooleanPrebuiltEntity
+
+ - kind: ConditionGroup
+ id: handle_retry_date_choice
+ conditions:
+ - id: user_wants_retry_dates
+ condition: =Topic.retryDateChoice = true
+ actions:
+ - kind: GotoAction
+ id: goto_form_on_date_retry
+ actionId: collect_time_off
+
+ elseActions:
+ - kind: SendActivity
+ id: end_on_no_date_retry
+ activity: No problem! Let me know if you need anything else.
+
+ - kind: CancelAllDialogs
+ id: cancel_on_no_date_retry
+
+ # ========================================================
+ # V3: Build list of dates and pass to Plugin
+ # ========================================================
+
+ # Map selected Time Off Type + Reason to Workday ReasonID
+ - kind: SetVariable
+ id: resolve_reason_id
+ variable: Topic.resolvedReasonID
+ value: =LookUp(Topic.TimeOffReasonConfig, ReasonKey = Topic.timeOffReason && TimeOffTypeID = Topic.timeOffType).ReasonID
+
+ - kind: SetVariable
+ id: set_time_off_comment
+ variable: Topic.timeOffComment
+ value: =If(IsBlank(Topic.leaveComment), "ess generated time off request", Topic.leaveComment)
+
+ # Initialize date list (empty string)
+ - kind: SetVariable
+ id: init_date_list
+ variable: Topic.dateList
+ value: =""
+
+ # Initialize loop counter
+ - kind: SetVariable
+ id: init_iterator
+ variable: Topic.newIterator
+ value: =0
+
+ # Set up loop variable
+ - kind: SetVariable
+ id: set_iterator
+ variable: Topic.iterator
+ value: =Topic.newIterator
+
+ # Calculate current date for this iteration
+ - kind: SetVariable
+ id: calc_current_date
+ variable: Topic.currentDate
+ value: =Text(DateAdd(DateValue(Topic.startDate), Topic.iterator, TimeUnit.Days), "yyyy-MM-dd")
+
+ # Loop to build comma-separated date list
+ - kind: ConditionGroup
+ id: build_date_list_loop
+ conditions:
+ - id: should_continue_building
+ condition: =DateValue(Topic.currentDate) <= DateValue(Topic.endDate)
+ actions:
+ # Append date to list (with comma separator if not first)
+ - kind: SetVariable
+ id: append_date
+ variable: Topic.dateList
+ value: =If(Topic.dateList = "", Topic.currentDate, Topic.dateList & "," & Topic.currentDate)
+
+ # Increment counter
+ - kind: SetVariable
+ id: increment_iterator
+ variable: Topic.newIterator
+ value: =Topic.newIterator + 1
+
+ # Continue loop
+ - kind: GotoAction
+ id: goto_build_loop
+ actionId: set_iterator
+
+ # Total days is the count from loop
+ - kind: SetVariable
+ id: calc_total_days
+ variable: Topic.totalDays
+ value: =Topic.newIterator
+
+ # Make single API call - Plugin will build XML entries from date list
+ - kind: BeginDialog
+ id: execute_workday_multiday
+ displayName: Submit Multi-Day Time Off Request
+ input:
+ binding:
+ parameters: |-
+ ="{""params"":[" &
+ "{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """}," &
+ "{""key"":""{timeoff_Dates}"",""value"":""" & Topic.dateList & """}," &
+ "{""key"":""{timeoff_Time_Off_Type}"",""value"":""" & Topic.timeOffType & """}," &
+ "{""key"":""{timeoff_Hours_Per_Day}"",""value"":""" & Topic.hoursPerDay & """}," &
+ "{""key"":""{timeoff_Comment}"",""value"":""" & Topic.timeOffComment & """}," &
+ "{""key"":""{timeoff_Reason}"",""value"":""" & Topic.resolvedReasonID & """}" &
+ "]}"
+ scenarioName: msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ # Parse error message for display
+ - kind: SetVariable
+ id: init_last_error
+ variable: Topic.lastErrorMessage
+ value: =If(Topic.isSuccess = true, "", Topic.errorResponse)
+
+ - kind: SetVariable
+ id: parse_error_message
+ variable: Topic.friendlyErrorMessage
+ value: =IfError(Text(ParseJSON(Topic.lastErrorMessage).error.message), IfError(Text(ParseJSON(Topic.lastErrorMessage).message), Topic.lastErrorMessage))
+
+ - kind: ConditionGroup
+ id: report_result
+ conditions:
+ - id: request_failed
+ condition: =Topic.isSuccess = false
+ actions:
+ # Use AI to generate a friendly, conversational error message
+ - kind: AnswerQuestionWithAI
+ id: generate_friendly_error
+ autoSend: false
+ variable: Topic.aiGeneratedError
+ userInput: =Topic.friendlyErrorMessage
+ additionalInstructions: Reframe the following Workday error message in a friendly way for an employee. Keep it to ONE short sentence describing what went wrong. Do NOT include suggestions or next steps. Do NOT apologize. Do NOT use technical jargon. Example output format - "The dates you selected conflict with an existing request."
+
+ # Set final error message with fallback
+ - kind: SetVariable
+ id: set_final_error_message
+ variable: Topic.finalErrorMessage
+ value: =If(IsBlank(Topic.aiGeneratedError), "The dates you selected aren't available.", Topic.aiGeneratedError)
+
+ - kind: SendActivity
+ id: friendly_error_message
+ activity: "{Topic.finalErrorMessage}"
+
+ # Ask user if they want to try again
+ - kind: Question
+ id: ask_retry
+ interruptionPolicy:
+ allowInterruption: false
+
+ variable: Topic.retryChoice
+ prompt: Would you like to try with different dates?
+ entity: BooleanPrebuiltEntity
+
+ - kind: ConditionGroup
+ id: handle_retry_choice
+ conditions:
+ - id: user_wants_retry
+ condition: =Topic.retryChoice = true
+ actions:
+ - kind: GotoAction
+ id: goto_form_on_retry
+ actionId: collect_time_off
+
+ elseActions:
+ - kind: SendActivity
+ id: end_on_no_retry
+ activity: No problem! Let me know if you need anything else.
+
+ - kind: CancelAllDialogs
+ id: cancel_on_no_retry
+
+ elseActions:
+ - kind: SendActivity
+ id: success_card
+ activity:
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "✅ Request submitted",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true
+ },
+ {
+ type: "TextBlock",
+ text: "Your time off request has been successfully submitted and is now pending approval.",
+ wrap: true,
+ spacing: "Small"
+ },
+ {
+ type: "Table",
+ spacing: "Medium",
+ showGridLines: false,
+ firstRowAsHeader: false,
+ columns: [
+ { width: 1 },
+ { width: 2 }
+ ],
+ rows: [
+ {
+ type: "TableRow",
+ cells: [
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: "Type of time off", weight: "Bolder" }]
+ },
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: Switch(Topic.timeOffType, "ESS_Vacation_Hours", "Vacation", "ESS_Floating_Holiday_Hours", "Floating holiday", "ESS_Sick_Time_Off", "Sick leave", "ESS_Comp_Off", "Comp off", Topic.timeOffType), wrap: true }]
+ }
+ ]
+ },
+ {
+ type: "TableRow",
+ cells: [
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: "Time off date range", weight: "Bolder" }]
+ },
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: Text(DateValue(Topic.startDate), "mmmm d, yyyy") & " to " & Text(DateValue(Topic.endDate), "mmmm d, yyyy") & " (" & Text(Topic.totalDays) & " days)", wrap: true }]
+ }
+ ]
+ },
+ {
+ type: "TableRow",
+ cells: [
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: "Total hours", weight: "Bolder" }]
+ },
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: Text(Topic.totalDays * Value(Topic.hoursPerDay)) & " hours (" & Text(Topic.totalDays) & " x " & Topic.hoursPerDay & "-hour days)", wrap: true }]
+ }
+ ]
+ },
+ {
+ type: "TableRow",
+ cells: [
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: "Reason", weight: "Bolder" }]
+ },
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: Switch(Topic.timeOffReason, "full_day", "Full Day", "half_day_am", "Half Day AM", "half_day_pm", "Half Day PM", Topic.timeOffReason), wrap: true }]
+ }
+ ]
+ },
+ {
+ type: "TableRow",
+ cells: [
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: "Comments", weight: "Bolder" }]
+ },
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: If(IsBlank(Topic.leaveComment), "—", Topic.leaveComment), wrap: true }]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "Container",
+ horizontalAlignment: "Right",
+ items: [
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "auto",
+ items: [
+ {
+ type: "Image",
+ url: Topic.WorkdayIconUrl,
+ style: "RoundedCorners",
+ size: "Small",
+ height: "20px",
+ width: "20px"
+ }
+ ],
+ verticalContentAlignment: "Center"
+ },
+ {
+ type: "Column",
+ width: "auto",
+ items: [
+ {
+ type: "TextBlock",
+ text: "Workday",
+ size: "Small",
+ color: "#242424",
+ weight: "Bolder"
+ }
+ ],
+ verticalContentAlignment: "Center",
+ spacing: "Small"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ actions: []
+ }
+
+ - kind: SendActivity
+ id: follow_up_msg
+ activity: Can I help you submit another request?
+
+ - kind: CancelAllDialogs
+ id: end_dialogs
+
+inputType:
+ properties:
+ EmployeeName:
+ displayName: EmployeeName
+ description: The name of the employee to fetch data for.
+ type: String
+
+ InputEndDate:
+ displayName: InputEndDate
+ description: The end date of the time off request.
+ type: DateTime
+
+ InputHoursPerDay:
+ displayName: InputHoursPerDay
+ description: The number of hours per day for the time off request.
+ type: Number
+
+ InputLeaveReason:
+ displayName: InputLeaveReason
+ description: Additional comments or context for the time off request.
+ type: String
+
+ InputStartDate:
+ displayName: InputStartDate
+ description: The start date of the time off request.
+ type: DateTime
+
+ InputTimeOffType:
+ displayName: InputTimeOffType
+ description: The type of time off being requested.
+ type: String
+
+outputType: {}
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeesviewtheirjobtaxonomy/msdyn_HRWorkdayHCMEmployeeGetJobTaxonomy.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeesviewtheirjobtaxonomy/msdyn_HRWorkdayHCMEmployeeGetJobTaxonomy.xml
new file mode 100644
index 00000000..627e54d8
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeesviewtheirjobtaxonomy/msdyn_HRWorkdayHCMEmployeeGetJobTaxonomy.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+ User
+
+ Template_GetWorkerRequest
+ Human_Resources
+ v42.0
+
+
+
+ //*[local-name()="Position_Title"]/text()
+ JobTitle
+
+
+ //*[local-name()="Business_Title"]/text()
+ BusinessTitle
+
+
+ //*[local-name()="Job_Profile_Name"]/text()
+ JobProfile
+
+
+ //*[local-name()='Job_Profile_Summary_Data']/*[local-name()='Job_Family_Reference']/*[local-name()='ID' and @*[local-name()='type']='Job_Family_ID']/text()
+ JobFamilyId
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeesviewtheirjobtaxonomy/topic.yaml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeesviewtheirjobtaxonomy/topic.yaml
new file mode 100644
index 00000000..cd80c742
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayEmployeesviewtheirjobtaxonomy/topic.yaml
@@ -0,0 +1,140 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ You will respond to requests about the job function of the user making the request. This data is retrieved from Workday. You will respond to the user based on which piece of data they are asking for. This data exclusively belongs to the user making the request. There is no data for anyone else. Do not respond to questions about other people's data.
+
+ Example invalid requests:
+ "What is my manager's job function?"
+ "What is my sister's job title?"
+
+ Example valid requests:
+ "What is my external title?"
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - What is my job title?
+ - What job function do I belong to?
+ - What is my job function?
+ - What job category do I belong to?
+ - What is my role?
+ - What job Family do I belong to?
+ - What is my Job Profile?
+ - What is my internal title?
+ - What is my external title?
+
+ actions:
+ - kind: BeginDialog
+ id: 7R6DUf
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetJobTaxonomy
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: tLP86E
+ displayName: Parse job profiles
+ variable: Topic.workdayResponseTable
+ valueType:
+ kind: Record
+ properties:
+ BusinessTitle:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ JobFamilyId:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ JobProfile:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ JobTitle:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: setVariable_FRHCD9
+ variable: Topic.transformResponse
+ value: |-
+ ={
+ JobTitle: First(Topic.workdayResponseTable.JobTitle).Value,
+ BusinessTitle: First(Topic.workdayResponseTable.BusinessTitle).Value,
+ JobProfile: First(Topic.workdayResponseTable.JobProfile).Value,
+ JobFamilyId: First(Topic.workdayResponseTable.JobFamilyId).Value
+ }
+
+ - kind: BeginDialog
+ id: RZJGDY
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Job_Family_ID}"",""value"":""" & Topic.transformResponse.JobFamilyId & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetJobTaxonomyGeneric
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: rPHxhw
+ displayName: Parse Job Family Name
+ variable: Topic.jobFamilyTable
+ valueType:
+ kind: Record
+ properties:
+ JobFamily:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: setVariable_mS3r3D
+ displayName: Init Job Taxonomy Data
+ variable: Topic.jobFunctionData
+ value: |-
+ ={
+ EmployeeName: Global.ESS_UserContext_Employee_Firstname & " " & Global.ESS_UserContext_Employee_Lastname,
+ JobTitle: Topic.transformResponse.JobTitle,
+ JobProfile: Topic.transformResponse.JobProfile,
+ BusinessTitle: Topic.transformResponse.BusinessTitle,
+ JobFamily: First(Topic.jobFamilyTable.JobFamily).Value
+ }
+
+outputType:
+ properties:
+ jobFunctionData:
+ displayName: jobFunctionData
+ type:
+ kind: Record
+ properties:
+ BusinessTitle: String
+ EmployeeName: String
+ JobFamily: String
+ JobProfile: String
+ JobTitle: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetContactInformation/msdyn_HRWorkdayHCMEmployeeGetHomeContactInformation.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetContactInformation/msdyn_HRWorkdayHCMEmployeeGetHomeContactInformation.xml
new file mode 100644
index 00000000..f218282a
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetContactInformation/msdyn_HRWorkdayHCMEmployeeGetHomeContactInformation.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+ User
+
+ msdyn_HRWorkdayHCMEmployeeGetHomeContactInformationRequest
+ Human_Resources
+ v42.0
+
+
+
+ //*[local-name()='Phone_Information_Data']
+ PhoneInformation
+
+
+ //*[local-name()='Email_Information_Data']
+ EmailInformation
+
+
+ //@*[local-name()='Formatted_Address']
+ HomeAddress
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+ {As_Of_Effective_Date}
+ 1
+ 999
+
+
+
+ false
+ false
+ false
+ false
+ false
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetContactInformation/topic.yaml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetContactInformation/topic.yaml
new file mode 100644
index 00000000..24c261ce
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetContactInformation/topic.yaml
@@ -0,0 +1,421 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ You will respond to requests about the contact information of the user making the request. This data is retrieved from Workday. You will respond to the user based on which piece of data they are asking for. This data exclusively belongs to the user making the request. There is no data for anyone else. Do not respond to questions about other people's data. "Home" and "Personal" data are synonymous.
+
+ Example invalid requests:
+ "What is my manager's contact information?"
+ "What is my sister's contact information?"
+
+ Example valid requests:
+ "What is my contact information?"
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: EmployeeName
+ description: The name of the employee whose data will be fetched
+ entity: PersonNamePrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - Show my Contact details?
+ - What is my Work Phone?
+ - What is my Work Email?
+ - What is my Work Address?
+ - Show my Work Contact information?
+ - Show my Personal Phone number?
+ - What is my Home Phone number?
+ - Show my Home Email?
+ - Can you tell me what my Personal Email is ?
+ - Which Personal email is registered as primary?
+ - Show my Home address?
+ - Show my Personal Contact Information?
+ - Show [EmployeeName]'s contact info
+
+ actions:
+ - kind: BeginDialog
+ id: uzpKxp
+ displayName: Check user access
+ input:
+ binding:
+ EmployeeName: =Topic.EmployeeName
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemAccessCheck
+
+ - kind: BeginDialog
+ id: Z6efSa
+ displayName: Fetch Work Contact Information from Workday
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: ="msdyn_HRWorkdayHCMEmployeeGetWorkContactInformation"
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: b90ZAp
+ displayName: Parse Work Contact data
+ variable: Topic.workContactData
+ valueType:
+ kind: Record
+ properties:
+ EmailInformation:
+ type:
+ kind: Table
+ properties:
+ Email_Data:
+ type:
+ kind: Record
+ properties:
+ Email_Address: String
+
+ Email_ID: String
+ Email_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Usage_Data:
+ type:
+ kind: Record
+ properties:
+ @Public: String
+ Type_Data:
+ type:
+ kind: Record
+ properties:
+ @Primary: String
+ Type_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ PhoneInformation:
+ type:
+ kind: Table
+ properties:
+ Phone_Data:
+ type:
+ kind: Record
+ properties:
+ @Formatted_Phone: String
+ Complete_Phone_Number: String
+ Country_Code_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Device_Type_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Phone_ID: String
+ Phone_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Usage_Data:
+ type:
+ kind: Table
+ properties:
+ @Public: String
+ Type_Data:
+ type:
+ kind: Record
+ properties:
+ @Primary: String
+ Type_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ value: =Topic.workdayResponse
+
+ - kind: BeginDialog
+ id: tZ3bkQ
+ displayName: Fetch Home Contact Information from Workday
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetHomeContactInformation
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: D15SKZ
+ displayName: Parse Home Contact data
+ variable: Topic.homeContactData
+ valueType:
+ kind: Record
+ properties:
+ EmailInformation:
+ type:
+ kind: Table
+ properties:
+ Email_Data:
+ type:
+ kind: Record
+ properties:
+ Email_Address: String
+ Email_Comment: String
+
+ Email_ID: String
+ Email_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Usage_Data:
+ type:
+ kind: Record
+ properties:
+ @Public: String
+ Type_Data:
+ type:
+ kind: Record
+ properties:
+ @Primary: String
+ Type_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ HomeAddress:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ PhoneInformation:
+ type:
+ kind: Table
+ properties:
+ Phone_Data:
+ type:
+ kind: Record
+ properties:
+ @Formatted_Phone: String
+ Complete_Phone_Number: String
+ Country_Code_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Device_Type_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Phone_ID: String
+ Phone_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Usage_Data:
+ type:
+ kind: Table
+ properties:
+ @Public: String
+ Type_Data:
+ type:
+ kind: Record
+ properties:
+ @Primary: String
+ Type_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ value: =Topic.workdayResponse
+
+ - kind: BeginDialog
+ id: p2xAZU
+ displayName: Fetch Work Address
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetWorkAddress
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: 3IdZeI
+ displayName: Parse Work Address
+ variable: Topic.workAddress
+ valueType:
+ kind: Record
+ properties:
+ WorkAddress:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: setVariable_LXnWnT
+ displayName: Set final data
+ variable: Topic.finalizedContactInformation
+ value: |-
+ ={
+ employeeName: Global.ESS_UserContext_Employee_Firstname & " " & Global.ESS_UserContext_Employee_Lastname,
+ workPhoneNumbers:ForAll(Topic.workContactData.PhoneInformation, {PhoneNumber: ThisRecord.Phone_Data.'@Formatted_Phone',IsPrimaryPhoneNumber: CountIf(ThisRecord.Usage_Data,Type_Data.'@Primary' = "1") > 0}),
+ workEmails:ForAll(Topic.workContactData.EmailInformation, {EmailAddress: ThisRecord.Email_Data.Email_Address, IsPrimaryEmailAddress:ThisRecord.Usage_Data.Type_Data.'@Primary'}),
+ workAddress: First(Topic.workAddress.WorkAddress).Value,
+ homePhoneNumbers: ForAll(Topic.homeContactData.PhoneInformation, {PhoneNumber: ThisRecord.Phone_Data.'@Formatted_Phone',IsPrimaryPhoneNumber: CountIf(ThisRecord.Usage_Data,Type_Data.'@Primary' = "1") > 0}),
+ homeEmails:ForAll(Topic.homeContactData.EmailInformation, {EmailAddress: ThisRecord.Email_Data.Email_Address, IsPrimaryEmailAddress:ThisRecord.Usage_Data.Type_Data.'@Primary'}),
+ homeAddress:First(Topic.homeContactData.HomeAddress).Value
+ }
+
+inputType:
+ properties:
+ EmployeeName:
+ displayName: EmployeeName
+ description: The name of the employee whose data will be fetched
+ type: String
+
+outputType:
+ properties:
+ finalizedContactInformation:
+ displayName: finalizedContactInformation
+ type:
+ kind: Record
+ properties:
+ employeeName: String
+ homeAddress: String
+ homeEmails:
+ type:
+ kind: Table
+ properties:
+ EmailAddress: String
+ IsPrimaryEmailAddress: String
+
+ homePhoneNumbers:
+ type:
+ kind: Table
+ properties:
+ IsPrimaryPhoneNumber: Boolean
+ PhoneNumber: String
+
+ workAddress: String
+ workEmails:
+ type:
+ kind: Table
+ properties:
+ EmailAddress: String
+ IsPrimaryEmailAddress: String
+
+ workPhoneNumbers:
+ type:
+ kind: Table
+ properties:
+ IsPrimaryPhoneNumber: Boolean
+ PhoneNumber: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetEducation/msdyn_HRWorkdayHCMEmployeeGetEducation.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetEducation/msdyn_HRWorkdayHCMEmployeeGetEducation.xml
new file mode 100644
index 00000000..d32311db
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetEducation/msdyn_HRWorkdayHCMEmployeeGetEducation.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+ User
+
+ HRWorkdayHCMEmployeeGetEducationRequest
+ Human_Resources
+ v41.0
+
+
+
+ //*[local-name()='Worker']/*[local-name()='Worker_Data']/*[local-name()='Qualification_Data']/*[local-name()='Education']
+ EducationData
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetEducation/topic.yaml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetEducation/topic.yaml
new file mode 100644
index 00000000..3b1b48c6
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetEducation/topic.yaml
@@ -0,0 +1,247 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ You will respond to requests about the education of the user making the request. This data is retrieved from Workday. You will respond to the user based on which piece of data they are asking for. This data exclusively belongs to the user making the request. There is no data for anyone else. Do not respond to questions about other people's data.
+
+ Example invalid requests:
+ "What is my manager's education?"
+ "What is my sister's education?"
+
+ Example valid requests:
+ "What is my education?"
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: EmployeeName
+ description: The name of the person whose employment data is being requested.
+ entity: PersonNamePrebuiltEntity
+ shouldPromptUser: false
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - Show my Education Details
+ - From which school I completed my BS degree Education?
+ - Show my Education Degrees
+ - What was my field of Study during Education?
+ - Show my First Year attended for BS degree Education?
+ - Show my Last Year attended for BS degree Education?
+
+ actions:
+ - kind: BeginDialog
+ id: zQU3vE
+ displayName: Check access
+ input:
+ binding:
+ EmployeeName: =Topic.EmployeeName
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemAccessCheck
+
+ - kind: BeginDialog
+ id: B7ae9T
+ displayName: Redirect to Get CommonExecution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetEducation
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: yJ0sSR
+ displayName: Parse Workday response
+ variable: Topic.parsedWorkdayResponse
+ valueType:
+ kind: Record
+ properties:
+ EducationData:
+ type:
+ kind: Table
+ properties:
+ Education_Data:
+ type:
+ kind: Table
+ properties:
+ Country_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Date_Degree_Received: String
+ Degree_Completed_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Degree_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Education_ID: String
+ Field_Of_Study_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ First_Year_Attended: String
+ Is_Highest_Level_of_Education: String
+ Last_Year_Attended: String
+ Location: String
+ School_Name: String
+ School_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Education_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ value: =Topic.workdayResponse
+
+ - kind: BeginDialog
+ id: M8G7as
+ displayName: Refresh country name reference data
+ input:
+ binding:
+ IsTableEmpty: =IsBlank(Global.CountryNameLookupTable)
+ ReferenceDataKey: ISO_3166-1_Alpha-2_Code
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemRefreshReferenceData
+
+ - kind: BeginDialog
+ id: iM5A5i
+ displayName: Refresh degree reference data
+ input:
+ binding:
+ IsTableEmpty: =IsBlank(Global.DegreeTypeLookupTable)
+ ReferenceDataKey: Degree_ID
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemRefreshReferenceData
+
+ - kind: BeginDialog
+ id: zG3du7
+ displayName: Refresh field of study reference data
+ input:
+ binding:
+ IsTableEmpty: =IsBlank(Global.FieldOfStudyLookupTable)
+ ReferenceDataKey: Field_Of_Study_ID
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemRefreshReferenceData
+
+ - kind: SetVariable
+ id: setVariable_nD5Gkb
+ displayName: Set final result data
+ variable: Topic.FinalizedEducationData
+ value: |
+ =ForAll(
+ Topic.parsedWorkdayResponse.EducationData,
+ ForAll(
+ ThisRecord.Education_Data,
+ With(
+ {currentRecord: ThisRecord},
+ {
+ EmployeeName: Global.ESS_UserContext_Employee_Firstname & " " & Global.ESS_UserContext_Employee_Lastname,
+ Country: LookUp(
+ Global.CountryNameLookupTable,
+ ID = LookUp(
+ currentRecord.Country_Reference.ID,
+ '@type' = First(Global.CountryNameLookupTable).Reference_ID_Type
+ ).'#text'
+ ).Referenced_Object_Descriptor,
+ School: currentRecord.School_Name,
+ Degree: LookUp(
+ Global.DegreeTypeLookupTable,
+ ID = LookUp(
+ currentRecord.Degree_Reference.ID,
+ '@type' = First(Global.DegreeTypeLookupTable).Reference_ID_Type
+ ).'#text'
+ ).Referenced_Object_Descriptor,
+ FieldOfStudy: LookUp(
+ Global.FieldOfStudyLookupTable,
+ ID = LookUp(
+ currentRecord.Field_Of_Study_Reference.ID,
+ '@type' = First(Global.FieldOfStudyLookupTable).Reference_ID_Type
+ ).'#text'
+ ).Referenced_Object_Descriptor,
+ FirstYearAttended: currentRecord.First_Year_Attended,
+ LastYearAttended: currentRecord.Last_Year_Attended
+ }
+ )
+ )
+ )
+
+inputType:
+ properties:
+ EmployeeName:
+ displayName: EmployeeName
+ description: The name of the person whose employment data is being requested.
+ type: String
+
+outputType:
+ properties:
+ FinalizedEducationData:
+ displayName: FinalizedEducationData
+ type:
+ kind: Table
+ properties:
+ Value:
+ type:
+ kind: Table
+ properties:
+ Country: String
+ Degree: String
+ EmployeeName: String
+ FieldOfStudy: String
+ FirstYearAttended: String
+ LastYearAttended: String
+ School: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetGovernmentIDs/msdyn_HRWorkdayHCMEmployeeGetGovernmentIds.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetGovernmentIDs/msdyn_HRWorkdayHCMEmployeeGetGovernmentIds.xml
new file mode 100644
index 00000000..3eca23e4
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetGovernmentIDs/msdyn_HRWorkdayHCMEmployeeGetGovernmentIds.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+ User
+
+ Template_GetPersonalInformationRequest
+ Human_Resources
+ v42.0
+
+
+
+ //*[local-name()='Government_ID']/*[local-name()='Government_ID_Data']
+ GovernmentId
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetGovernmentIDs/topic.yaml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetGovernmentIDs/topic.yaml
new file mode 100644
index 00000000..f7a6fa9f
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetGovernmentIDs/topic.yaml
@@ -0,0 +1,165 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ You will respond to requests about the government ids of the user making the request. This data is retrieved from Workday. You will respond to the user based on which piece of data they are asking for. This data exclusively belongs to the user making the request. There is no data for anyone else. Do not respond to questions about other people's data.
+
+ Example invalid requests:
+ "What are my manager's government ids?"
+ "What are my sister's government ids?"
+
+ Example valid requests:
+ "What are my government ids?"
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: EmployeeName
+ description: The name of the employee whose data is being requested.
+ entity: PersonNamePrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - What are my Government Ids?
+ - Which Government ids are available on my profile?
+ - Show my Government Ids?
+ - Show me [EmployeeName]'s government Ids
+
+ actions:
+ - kind: BeginDialog
+ id: GlRaX3
+ displayName: Check user access
+ input:
+ binding:
+ EmployeeName: =Topic.EmployeeName
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemAccessCheck
+
+ - kind: BeginDialog
+ id: 3bFDxH
+ displayName: Redirect to Workday System Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetGovernmentIds
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: v0OBu2
+ displayName: Parse workdayResponse into table
+ variable: Topic.parsedWorkdayResponse
+ valueType:
+ kind: Record
+ properties:
+ GovernmentId:
+ type:
+ kind: Table
+ properties:
+ Country_Reference:
+ type:
+ kind: Record
+ properties:
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Expiration_Date: String
+ ID: String
+ ID_Type_Reference:
+ type:
+ kind: Record
+ properties:
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Issued_Date: String
+
+ value: =Topic.workdayResponse
+
+ - kind: BeginDialog
+ id: 6k3mYE
+ displayName: Refresh country name reference data
+ input:
+ binding:
+ IsTableEmpty: =IsBlank(Global.CountryNameLookupTable)
+ ReferenceDataKey: ISO_3166-1_Alpha-2_Code
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemRefreshReferenceData
+
+ - kind: BeginDialog
+ id: Xoydcu
+ displayName: Refresh government ID type data
+ input:
+ binding:
+ IsTableEmpty: =IsBlank(Global.GovernmentIdTypeLookupTable)
+ ReferenceDataKey: Government_ID_Type_ID
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemRefreshReferenceData
+
+ - kind: SetVariable
+ id: setVariable_ZYrAxQ
+ displayName: Get values from reference tables
+ variable: Topic.finalizedResponseTableData
+ value: |-
+ =ForAll(
+ Topic.parsedWorkdayResponse.GovernmentId,
+ With(
+ {currentRecord: ThisRecord},
+ {
+ EmployeeName: Global.ESS_UserContext_Employee_Firstname & " " & Global.ESS_UserContext_Employee_Lastname,
+ CountryName: LookUp(
+ Global.CountryNameLookupTable,
+ ID = LookUp(
+ currentRecord.Country_Reference.ID,
+ '@type' = First(Global.CountryNameLookupTable).Reference_ID_Type
+ ).'#text'
+ ).Referenced_Object_Descriptor,
+ ExpirationDate: currentRecord.Expiration_Date,
+ IDType: LookUp(
+ Global.GovernmentIdTypeLookupTable,
+ ID = LookUp(
+ currentRecord.ID_Type_Reference.ID,
+ '@type' = First(Global.GovernmentIdTypeLookupTable).Reference_ID_Type
+ ).'#text'
+ ).Referenced_Object_Descriptor,
+ IssuedDate: currentRecord.Issued_Date,
+ ID: ID
+ }
+ )
+ )
+
+inputType:
+ properties:
+ EmployeeName:
+ displayName: EmployeeName
+ description: The name of the employee whose data is being requested.
+ type: String
+
+outputType:
+ properties:
+ finalizedResponseTableData:
+ displayName: finalizedResponseTableData
+ type:
+ kind: Table
+ properties:
+ CountryName: String
+ EmployeeName: String
+ ExpirationDate: String
+ ID: String
+ IDType: String
+ IssuedDate: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetUserProfile/README.md b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetUserProfile/README.md
new file mode 100644
index 00000000..ccb49768
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetUserProfile/README.md
@@ -0,0 +1,168 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Get User Profile
+
+This scenario enables employees to retrieve their profile information from Workday through a conversational interface. It provides comprehensive employee data including personal details, employment information, contact details, and tenure calculations.
+
+## Features
+
+- **Personal Information**: Name, Date of Birth, Gender
+- **Employment Details**: Employee ID, Business Title, Organization/Department, Manager, Location
+- **Contact Information**: Work Email, Work Phone, Home Email, Home Phone, Home Address
+- **Employment Status**: Active/Inactive status, Hire Date
+- **Tenure Calculation**: Continuous Service Date and calculated Length of Service (years, months, days)
+
+## Trigger Phrases
+
+Users can activate this topic with phrases like:
+- "What is my profile?"
+- "Show my profile"
+- "What is my employee ID?"
+- "What is my job title?"
+- "What is my work email?"
+- "Who is my manager?"
+- "What department am I in?"
+- "What is my tenure?"
+- "How long have I been with the company?"
+- "What is my hire date?"
+- "Am I an active employee?"
+
+## Files
+
+| File | Description |
+|------|-------------|
+| `topic.yaml` | Main Copilot Studio topic definition |
+| `msdyn_HRWorkdayHCMEmployeeGetUserProfile.xml` | XML template with XPath extractions for profile data |
+
+## Workday APIs Used
+
+| API | Service | Version | Purpose |
+|-----|---------|---------|---------|
+| `Get_Workers` | Human_Resources | v45.0 | Retrieve comprehensive employee profile data |
+
+## Data Retrieved
+
+The topic extracts the following fields from Workday:
+
+| Field | XPath Source | Description |
+|-------|--------------|-------------|
+| `EmployeeID` | Worker_Data/Worker_ID | Employee's Workday ID |
+| `Name` | Worker_Descriptor | Employee's full name |
+| `DOB` | Personal_Information_Data/Birth_Date | Date of birth |
+| `Gender` | Gender_Reference/@Descriptor | Gender |
+| `BusinessTitle` | Position_Data/Business_Title | Job title |
+| `Organization` | Organization_Reference (SUPERVISORY_ORGANIZATION) | Department/Org |
+| `Manager` | Manager_Reference/@Descriptor | Direct manager's name |
+| `Location` | Location_Reference/@Descriptor | Work location |
+| `HireDate` | Worker_Status_Data/Hire_Date | Original hire date |
+| `WorkEmail` | Email_Address (WORK usage type) | Work email address |
+| `HomeAddress` | Address_Data (HOME usage type) | Formatted home address |
+| `HomeEmail` | Email_Address (HOME usage type, primary) | Personal email |
+| `HomePhone` | Phone_Data (HOME usage type, primary) | Home phone number |
+| `WorkPhone` | Phone_Data (WORK usage type, primary) | Work phone number |
+| `Status` | Worker_Status_Data/Active | Employment status (Active/Inactive) |
+| `ContinuousServiceDate` | Worker_Status_Data/Continuous_Service_Date | Service start date |
+| `LengthOfService` | *Calculated* | Years, months, days of service |
+
+## Flow Overview
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ User Triggers Topic │
+│ (e.g., "What is my profile?") │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Call Get_Workers API │
+│ (with Employee_ID and As_Of_Date) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Parse Response via XPath │
+│ (Extract all profile fields) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Calculate Length of Service │
+│ (Years, Months, Days from Continuous Service Date) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Return finalizedData Record │
+│ (All fields available for AI to respond) │
+└─────────────────────────────────────────────────────────────┘
+```
+
+## Length of Service Calculation
+
+The topic automatically calculates the employee's length of service from their Continuous Service Date:
+
+```
+Years: RoundDown(DateDiff(ServiceDate, Today, Months) / 12, 0)
+Months: Mod(DateDiff(ServiceDate, Today, Months), 12)
+Days: DateDiff(AdjustedDate, Today, Days)
+```
+
+Output format: "X year(s) Y month(s) Z day(s)"
+
+## Dependencies
+
+This topic requires the following system topics/dialogs:
+- `msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution` - For executing Workday API calls
+
+## Global Variables Used
+
+| Variable | Description |
+|----------|-------------|
+| `Global.ESS_UserContext_Employee_Id` | The logged-in employee's Workday Employee ID |
+
+## Output
+
+The topic outputs a `finalizedData` record containing all profile fields that can be used by the AI orchestrator to formulate responses based on what the user specifically asked for.
+
+```yaml
+outputType:
+ properties:
+ finalizedData:
+ type: Record
+ properties:
+ EmployeeID: String
+ Name: String
+ DOB: String
+ Gender: String
+ BusinessTitle: String
+ Organization: String
+ Manager: String
+ Location: String
+ HireDate: String
+ WorkEmail: String
+ HomeAddress: String
+ HomeEmail: String
+ HomePhone: String
+ WorkPhone: String
+ Status: String
+ ContinuousServiceDate: String
+ LengthOfService: String
+```
+
+## Important Notes
+
+1. **Privacy**: This topic only returns data for the requesting user. Questions about other employees (managers, colleagues) are explicitly rejected per the model description.
+
+2. **Tenure Information**: Length of Service is only included in the AI's response when the user specifically asks about tenure, service length, or how long they've been with the company.
+
+3. **Status Conversion**: The raw `Active` field from Workday (1 or 0) is converted to human-readable "Active" or "Inactive".
+
+4. **Response Optimization**: The Get_Workers request is optimized to exclude unnecessary data (benefits, qualifications, photos, etc.) to improve performance.
+
+## Version History
+
+| Version | Date | Changes |
+|---------|------|---------|
+| 1.0 | December 2025 | Initial release with comprehensive profile retrieval |
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetUserProfile/msdyn_HRWorkdayHCMEmployeeGetUserProfile.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetUserProfile/msdyn_HRWorkdayHCMEmployeeGetUserProfile.xml
new file mode 100644
index 00000000..37412e3d
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetUserProfile/msdyn_HRWorkdayHCMEmployeeGetUserProfile.xml
@@ -0,0 +1,131 @@
+
+
+
+
+
+ User
+
+ Template_GetWorkerRequest
+ Human_Resources
+ v45.0
+
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Worker_ID']/text()
+ EmployeeID
+
+
+ //*[local-name()='Get_Workers_Response']/*[local-name()='Response_Data']/*[local-name()='Worker'][1]/*[local-name()='Worker_Descriptor']/text()
+ Name
+
+
+ //*[local-name()='Personal_Information_Data']/*[local-name()='Birth_Date']/text()
+ DOB
+
+
+ //*[local-name()='Personal_Information_For_Country_Data']/*[local-name()='Country_Personal_Information_Data']/*[local-name()='Gender_Reference']/@*[local-name()='Descriptor']
+ Gender
+
+
+ //*[local-name()='Position_Data']/*[local-name()='Business_Title']/text()
+ BusinessTitle
+
+
+ //*[local-name()='Worker_Organization_Data']/*[local-name()='Organization_Reference']/*[local-name()='ID'][@*[local-name()='type']='Organization_Reference_ID' and contains(text(),'SUPERVISORY_ORGANIZATION')]/../@*[local-name()='Descriptor']
+ Organization
+
+
+ //*[local-name()='Worker_Supervisory_Management_Chain_Data']/*[local-name()='Management_Chain_Data']/*[local-name()='Manager_Reference']/@*[local-name()='Descriptor']
+ Manager
+
+
+ //*[local-name()='Business_Site_Summary_Data']/*[local-name()='Location_Reference']/*[local-name()='ID'][@*[local-name()='type']='Location_ID']/../@*[local-name()='Descriptor']
+ Location
+
+
+ //*[local-name()='Worker_Status_Data']/*[local-name()='Hire_Date']/text()
+ HireDate
+
+
+ //*[local-name()='Email_Address_Data'][*[local-name()='Usage_Data']/*[local-name()='Type_Data']/*[local-name()='Type_Reference']/*[local-name()='ID'][@*[local-name()='type']='Communication_Usage_Type_ID' and text()='WORK']]/*[local-name()='Email_Address']/text()
+ WorkEmail
+
+
+ //*[local-name()='Address_Data'][*[local-name()='Usage_Data']/*[local-name()='Type_Data']/*[local-name()='Type_Reference']/*[local-name()='ID'][@*[local-name()='type']='Communication_Usage_Type_ID' and text()='HOME']]/@*[local-name()='Formatted_Address']
+ HomeAddress
+
+
+ //*[local-name()='Email_Address_Data'][*[local-name()='Usage_Data']/*[local-name()='Type_Data'][@*[local-name()='Primary']='1']/*[local-name()='Type_Reference']/*[local-name()='ID'][@*[local-name()='type']='Communication_Usage_Type_ID' and text()='HOME']]/*[local-name()='Email_Address']/text()
+ HomeEmail
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Contact_Data']/*[local-name()='Phone_Data'][*[local-name()='Usage_Data']/*[local-name()='Type_Data'][@*[local-name()='Primary']='1']/*[local-name()='Type_Reference']/*[local-name()='ID'][@*[local-name()='type']='Communication_Usage_Type_ID' and text()='HOME']]/@*[local-name()='Tenant_Formatted_Phone']
+ HomePhone
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Contact_Data']/*[local-name()='Phone_Data'][*[local-name()='Usage_Data']/*[local-name()='Type_Data'][@*[local-name()='Primary']='1']/*[local-name()='Type_Reference']/*[local-name()='ID'][@*[local-name()='type']='Communication_Usage_Type_ID' and text()='WORK']]/@*[local-name()='Tenant_Formatted_Phone']
+ WorkPhone
+
+
+ //*[local-name()='Worker_Status_Data']/*[local-name()='Active']/text()
+ Status
+
+
+ //*[local-name()='Worker_Status_Data']/*[local-name()='Continuous_Service_Date']/text()
+ ContinuousServiceDate
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ false
+ true
+ true
+ false
+ true
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+
+
+
+
+
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetUserProfile/topic.yaml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetUserProfile/topic.yaml
new file mode 100644
index 00000000..ea335a26
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGetUserProfile/topic.yaml
@@ -0,0 +1,232 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ Available fields: Name, Emp ID, DOB, Gender, Title, Organization, Manager, Location, Hire Date, Email, Phone, Home Address, Employment Status
+ For FULL PROFILE:
+ Heading = "Your employee profile"
+ Introduction = "Here's what I found in Workday, an HR platform your company uses."
+ Sections: Personal information, Hiring details, Role and work.
+
+ For TENURE/POSITION:
+ Heading = "Time in your position"
+ Introduction = "You've been in your current role as a [Position] for [LengthOfService]. I pulled this from Workday, an HR platform your company uses."
+ Sections: "About your position"
+
+ Common Rules ALWAYS FOLLOW:
+ - Add large sized heading with sentence case, add section line divider BELOW it
+ - ALWAYS include bold sections headings as mentioned above & add non-bold sub bullets under each
+ - ADDRESS IN ONE ROW
+ - Introduction right after heading
+ - Answer only based on what the user explicitly asks
+ - Include only relevant fields, not the full profile unless requested
+ - Double check if rules are followed, fix if not
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - What is my profile?
+ - Show my profile
+ - What is my employee ID?
+ - What is my job title?
+ - What is my work email?
+ - What is my manager's name?
+ - Who is my manager?
+ - What department am I in?
+ - What is my organization?
+ - What is my work phone number?
+ - What is my home address?
+ - What is my hire date?
+ - When did I start working here?
+ - What is my tenure?
+ - How long have I been with the company?
+ - What is my length of service?
+ - Am I an active employee?
+ - What is my employment status?
+ - What is my date of birth?
+ - What is my gender?
+ - What is my location?
+ - Where do I work?
+
+ actions:
+ - kind: BeginDialog
+ id: dZAI4Y
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetUserProfile
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: fP9fKn
+ displayName: Parse workdayResponse
+ variable: Topic.parsedWorkdayResponse
+ valueType:
+ kind: Record
+ properties:
+ EmployeeID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ Name:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ DOB:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ Gender:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ BusinessTitle:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ Organization:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ Manager:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ Location:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ HireDate:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ WorkEmail:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ HomeAddress:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ HomeEmail:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ HomePhone:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ WorkPhone:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ Status:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ ContinuousServiceDate:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: setVariable_ServiceDate
+ displayName: Calculate service date values
+ variable: Topic.serviceDateCalc
+ value: =DateValue(First(Topic.parsedWorkdayResponse.ContinuousServiceDate).Value)
+
+ - kind: SetVariable
+ id: setVariable_Years
+ displayName: Calculate years
+ variable: Topic.yearsOfService
+ value: =RoundDown(DateDiff(Topic.serviceDateCalc, Today(), TimeUnit.Months) / 12, 0)
+
+ - kind: SetVariable
+ id: setVariable_Months
+ displayName: Calculate months
+ variable: Topic.monthsOfService
+ value: =Mod(DateDiff(Topic.serviceDateCalc, Today(), TimeUnit.Months), 12)
+
+ - kind: SetVariable
+ id: setVariable_Days
+ displayName: Calculate days
+ variable: Topic.daysOfService
+ value: =DateDiff(DateAdd(Topic.serviceDateCalc, DateDiff(Topic.serviceDateCalc, Today(), TimeUnit.Months), TimeUnit.Months), Today(), TimeUnit.Days)
+
+ - kind: SetVariable
+ id: setVariable_LHTcFu
+ displayName: Set finalized data
+ variable: Topic.finalizedData
+ value: |-
+ ={
+ EmployeeID: First(Topic.parsedWorkdayResponse.EmployeeID).Value,
+ Name: First(Topic.parsedWorkdayResponse.Name).Value,
+ DOB: First(Topic.parsedWorkdayResponse.DOB).Value,
+ Gender: First(Topic.parsedWorkdayResponse.Gender).Value,
+ BusinessTitle: First(Topic.parsedWorkdayResponse.BusinessTitle).Value,
+ Organization: First(Topic.parsedWorkdayResponse.Organization).Value,
+ Manager: First(Topic.parsedWorkdayResponse.Manager).Value,
+ Location: First(Topic.parsedWorkdayResponse.Location).Value,
+ HireDate: First(Topic.parsedWorkdayResponse.HireDate).Value,
+ WorkEmail: First(Topic.parsedWorkdayResponse.WorkEmail).Value,
+ HomeAddress: Substitute(Substitute(Concat(Topic.parsedWorkdayResponse.HomeAddress, Value, ", "), Char(10), ", "), Char(13), ""),
+ HomeEmail: First(Topic.parsedWorkdayResponse.HomeEmail).Value,
+ HomePhone: First(Topic.parsedWorkdayResponse.HomePhone).Value,
+ WorkPhone: First(Topic.parsedWorkdayResponse.WorkPhone).Value,
+ Status: If(First(Topic.parsedWorkdayResponse.Status).Value = "1", "Active", "Inactive"),
+ ContinuousServiceDate: First(Topic.parsedWorkdayResponse.ContinuousServiceDate).Value,
+ LengthOfService:
+ If(Topic.yearsOfService > 0, Topic.yearsOfService & " year" & If(Topic.yearsOfService > 1, "s", "") & " ", "") &
+ If(Topic.monthsOfService > 0, Topic.monthsOfService & " month" & If(Topic.monthsOfService > 1, "s", "") & " ", "") &
+ Topic.daysOfService & " day" & If(Topic.daysOfService <> 1, "s", "")
+ }
+
+inputType: {}
+outputType:
+ properties:
+ finalizedData:
+ displayName: finalizedData
+ type:
+ kind: Record
+ properties:
+ EmployeeID: String
+ Name: String
+ DOB: String
+ Gender: String
+ BusinessTitle: String
+ Organization: String
+ Manager: String
+ Location: String
+ HireDate: String
+ WorkEmail: String
+ HomeAddress: String
+ HomeEmail: String
+ HomePhone: String
+ WorkPhone: String
+ Status: String
+ ContinuousServiceDate: String
+ LengthOfService: String
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGivePeerFeedback/msdyn_HRWorkdayHCMEmployeeGiveFeedback.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGivePeerFeedback/msdyn_HRWorkdayHCMEmployeeGiveFeedback.xml
new file mode 100644
index 00000000..6d30bdaf
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGivePeerFeedback/msdyn_HRWorkdayHCMEmployeeGiveFeedback.xml
@@ -0,0 +1,60 @@
+
+
+
+
+ User
+
+ msdyn_HRWorkdayHCMEmployeeGiveFeedback
+ Talent
+ v42.0
+
+
+
+ Employee_ID
+ {Employee_ID}
+
+
+ Employee_ID_Of_Receiver
+ {Employee_ID_Of_Receiver}
+
+
+ Comment
+ {Comment}
+
+
+
+
+ WID
+
+ //*[local-name()='Give_Feedback_Response']
+ /*[local-name()='Give_Feedback_Event_Reference']
+ /*[local-name()='ID'][@*[local-name()='type']='WID']/text()
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+
+
+
+ {Employee_ID}
+
+
+ {Employee_ID_Of_Receiver}
+
+ {Comment}
+ true
+ false
+ false
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGivePeerFeedback/topic.yaml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGivePeerFeedback/topic.yaml
new file mode 100644
index 00000000..6ca7bb66
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayGivePeerFeedback/topic.yaml
@@ -0,0 +1,83 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ This topic provides a way to send feedback to the coworkers.
+
+ Some of the valid requests are as follows:
+
+ give feedback
+ share feedback for a colleague
+ submit peer feedback
+ feedback for employee
+ I want to appreciate my teammate
+ report feedback about coworker
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - send feedback about workday
+ - give feedback on workday
+ - workday feedback
+ - report issue with workday
+ - share thoughts on workday
+ - submit comments for workday
+ - provide input on workday experience
+ - suggest improvements for workday
+ - complain about workday
+
+ actions:
+ - kind: Question
+ id: Question_KkmuHF
+ variable: Topic.varEmployeeId
+ prompt: What is the _Employee ID_ of the colleague you want to give feedback for?
+ entity: StringPrebuiltEntity
+
+ - kind: ConditionGroup
+ id: ConditionGroup_8NOr8O
+ conditions:
+ - id: ConditionItem_GLZBTd
+ condition: =!IsBlank(Topic.varEmployeeId)
+ actions:
+ - kind: Question
+ id: Question_ZGYUYN
+ variable: Topic.FeedbackText
+ prompt: Please enter your feedback for your coworker.
+ entity: StringPrebuiltEntity
+
+ - kind: BeginDialog
+ id: Za15eH
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """}, {""key"":""{Employee_ID_Of_Receiver}"",""value"":"""& Topic.varEmployeeId & """}, {""key"":""{Comment}"",""value"":""" & Topic.FeedbackText & """}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGiveFeedback
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ConditionGroup
+ id: conditionGroup_r9LEup
+ conditions:
+ - id: conditionItem_XVznZc
+ condition: =IsBlank(Topic.errorResponse) && Topic.isSuccess = true
+ actions:
+ - kind: SendActivity
+ id: sendActivity_ISzuar
+ activity: Feedback is sent successfully
+
+ elseActions:
+ - kind: SendActivity
+ id: sendActivity_qhWHOF
+ activity: There is a failure in sending the feedback {Topic.errorResponse}
+
+ elseActions:
+ - kind: SendActivity
+ id: SendActivity_jLD6lX
+ activity: You cannot send feedback to yourself.
+
+inputType: {}
+outputType: {}
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/README.md b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/README.md
new file mode 100644
index 00000000..2a518385
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/README.md
@@ -0,0 +1,104 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Manage Emergency Contact
+
+## Overview
+
+This topic enables employees to manage their emergency contacts in Workday through a conversational interface. Employees can view existing contacts, add new emergency contacts, update existing ones, and delete contacts they no longer need.
+
+## Features
+
+- View existing emergency contacts with primary contact highlighted
+- Add new emergency contacts with full details
+- Update details of existing emergency contacts
+- Delete non-primary emergency contacts
+- Set or change the primary emergency contact
+- Assign priority levels (2-10) to contacts
+
+## Snapshots
+
+
+
+
+
+## Trigger Phrases
+
+- "Manage my emergency contacts"
+- "Update my emergency contact"
+- "Add emergency contact"
+- "Change my emergency contact information"
+- "Delete my emergency contact"
+- "Show my emergency contacts"
+
+## Files
+
+| File | Description |
+|------|-------------|
+| `topic.yaml` | Copilot Studio topic definition with conversation flow |
+| `msdyn_HRWorkdayHCMEmployeeGetEmergencyContactInfo.xml` | XML template to fetch existing emergency contacts |
+| `msdyn_HRWorkdayHCMEmployeeAddEmergencyContact.xml` | XML template to add a new emergency contact |
+| `msdyn_HRWorkdayHCMEmployeeUpdateEmergencyContact.xml` | XML template to update an existing emergency contact |
+| `msdyn_HRWorkdayDeleteEmergencyContact.xml` | XML template to delete an emergency contact |
+
+## Workday APIs Used
+
+| API | Purpose |
+|-----|---------|
+| `Get_Workers` | Retrieve employee's existing emergency contacts |
+| `Change_Emergency_Contacts` | Add, update, or delete emergency contact information |
+
+## Flow Overview
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ User Triggers Topic │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Fetch Reference Data (Country Codes, Relationships) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Fetch Existing Emergency Contacts │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Show Selection Card (or Go to Add if no contacts) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Show Add/Update Form (Adaptive Card) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Submit to Workday │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Show Success/Error Message │
+└─────────────────────────────────────────────────────────────┘
+```
+
+## Configurations
+
+Environment makers need to configure the following in the topic:
+
+| Configuration | Description | Location in Topic |
+|---------------|-------------|-------------------|
+| **Relationship Types** | Available relationship options from Workday reference data | Dynamic from GetReferenceData |
+| **Country Codes** | Phone country codes from Workday reference data | Dynamic from GetReferenceData |
+| **States/Provinces** | Available state/province codes per country | Adaptive card dropdown |
+| **Workday Icon** | Update the icon URL to match your organization's branding | Topic properties > Icon |
+| **Workday URL** | Set your organization's Workday tenant URL | HTTP action or connector configuration |
+
+## Dependencies
+
+- **Employee Context**: Worker ID must be available in the conversation context
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayDeleteEmergencyContact.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayDeleteEmergencyContact.xml
new file mode 100644
index 00000000..0a762798
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayDeleteEmergencyContact.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+ User
+
+ Template_DeleteEmergencyContactRequest
+ Human_Resources
+ v45.0
+
+
+
+ //*[local-name()='Emergency_Contact_Event_Reference']/*[local-name()='ID'][@*[local-name()='type']='WID']/text()
+ EventID
+
+
+
+
+
+
+
+
+
+ false
+ true
+ true
+
+ {Comment}
+
+ {Employee_ID}
+
+
+
+
+
+ {Employee_ID}
+
+ false
+
+
+ {Emergency_Contact_WID}
+
+ true
+
+ false
+ {Priority}
+
+ {Relationship_Type}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeAddEmergencyContact.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeAddEmergencyContact.xml
new file mode 100644
index 00000000..8d4bcf05
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeAddEmergencyContact.xml
@@ -0,0 +1,101 @@
+
+
+
+
+ User
+
+ Template_AddEmergencyContactRequest
+ Human_Resources
+ v45.0
+
+
+
+ //*[local-name()='Emergency_Contact_Event_Reference']/*[local-name()='ID'][@*[local-name()='type']='WID']/text()
+ EventID
+
+
+
+
+
+
+
+
+
+ true
+ true
+ true
+
+ {Comment}
+
+ {Employee_ID}
+
+
+
+
+
+ {Employee_ID}
+
+ false
+
+ false
+
+ {Primary}
+ {Priority}
+
+ {Relationship_Type}
+
+
+
+
+
+
+ {Country_Code}
+
+ {First_Name}
+ {Last_Name}
+
+
+
+
+
+
+ {Address_Country_Code}
+
+ {Address_Line_1}
+ {City}
+
+ {State_Province}
+
+ {Postal_Code}
+
+
+
+ HOME
+
+
+
+
+
+ {Address_Country_Code}
+ {Phone_Country_Code}
+ {Phone_Number}
+
+ {Phone_Device_Type}
+
+
+
+
+ HOME
+
+
+
+ =
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeGetEmergencyContactInfo.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeGetEmergencyContactInfo.xml
new file mode 100644
index 00000000..eb8c0af2
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeGetEmergencyContactInfo.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+ User
+
+ Template_GetEmergencyContactInfoRequest
+ Human_Resources
+ v45.0
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Emergency_Contact']]
+ EmergencyContacts
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+
+
+
+
+
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeUpdateEmergencyContact.xml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeUpdateEmergencyContact.xml
new file mode 100644
index 00000000..e6928dc4
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeUpdateEmergencyContact.xml
@@ -0,0 +1,105 @@
+
+
+
+
+
+ User
+
+ Template_UpdateEmergencyContactRequest
+ Human_Resources
+ v45.0
+
+
+
+ //*[local-name()='Emergency_Contact_Event_Reference']/*[local-name()='ID'][@*[local-name()='type']='WID']/text()
+ EventID
+
+
+
+
+
+
+
+
+
+ false
+ true
+ true
+
+ {Comment}
+
+ {Employee_ID}
+
+
+
+
+
+ {Employee_ID}
+
+ false
+
+
+ {Emergency_Contact_WID}
+
+ false
+
+ {Primary}
+ {Priority}
+
+ {Relationship_Type}
+
+
+
+
+
+
+ {Country_Code}
+
+ {First_Name}
+ {Last_Name}
+
+
+
+
+
+
+ {Address_Country_Code}
+
+ {Address_Line_1}
+ {City}
+
+ {State_Province}
+
+ {Postal_Code}
+
+
+
+ HOME
+
+
+
+
+
+ {Address_Country_Code}
+ {Phone_Country_Code}
+ {Phone_Number}
+
+ {Phone_Device_Type}
+
+
+
+
+ HOME
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/topic.yaml b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/topic.yaml
new file mode 100644
index 00000000..00db22a1
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/topic.yaml
@@ -0,0 +1,1364 @@
+kind: AdaptiveDialog
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: InputAction
+ description: "Look for keywords 'add', 'new', or 'create' in the user's request. If found, extract 'add'. Look for keywords 'get', 'view', 'show', 'list', 'see', 'display', or 'what are' in the user's request. If found, extract 'get'. Look for keywords 'delete' or 'remove' in the user's request. If found, extract 'delete'. Otherwise extract 'manage'."
+ entity: StringPrebuiltEntity
+ shouldPromptUser: false
+
+modelDescription: |-
+ Manage emergency contacts for the requesting user only. Supports add and update via Workday (Human_Resources). Reject requests about other people's data.
+ Formatting: ALWAYS display contacts in a markdown table with columns: Name, Relationship, Phone, Address. Mark primary with ★. Sort primary first, then by priority. Use "—" for missing fields. Heading: "Your emergency contacts" with divider. Never use bullet lists. Hide internal IDs (WID, Priority number, Country Codes). Show ALL contacts.
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent: {}
+ actions:
+ - kind: SetVariable
+ id: set_workday_icon_url_2
+ variable: Topic.WorkdayIconUrl
+ value: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=
+
+ - kind: ConditionGroup
+ id: check_country_codes
+ conditions:
+ - id: country_codes_uninitialized
+ condition: =IsBlank(Global.CountryCodeLookupTable)
+ displayName: If country code picklist is uninitialized
+ actions:
+ - kind: BeginDialog
+ id: fetch_country_codes
+ displayName: Redirect to Workday System Get Reference Data
+ input:
+ binding:
+ referenceDataKey: Country_Phone_Code_ID
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.GetReferenceData
+
+ - kind: ConditionGroup
+ id: check_relationship_types
+ conditions:
+ - id: relationship_types_uninitialized
+ condition: =IsBlank(Global.RelatedPersonRelationshipLookupTable)
+ displayName: If relationship type picklist is uninitialized
+ actions:
+ - kind: BeginDialog
+ id: fetch_relationship_types
+ displayName: Redirect to Workday System Get Reference Data
+ input:
+ binding:
+ referenceDataKey: Related_Person_Relationship_ID
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.GetReferenceData
+
+ - kind: ConditionGroup
+ id: check_state_province_choices
+ conditions:
+ - id: state_choices_uninitialized
+ condition: =IsBlank(Global.StateProvinceChoices)
+ displayName: If state/province choices are uninitialized
+ actions:
+ - kind: SetVariable
+ id: init_state_province_choices
+ variable: Global.StateProvinceChoices
+ value: |
+ =[
+ { title: "Alabama", value: "USA-AL", country: "USA" },
+ { title: "Alaska", value: "USA-AK", country: "USA" },
+ { title: "Arizona", value: "USA-AZ", country: "USA" },
+ { title: "Arkansas", value: "USA-AR", country: "USA" },
+ { title: "California", value: "USA-CA", country: "USA" },
+ { title: "Colorado", value: "USA-CO", country: "USA" },
+ { title: "Connecticut", value: "USA-CT", country: "USA" },
+ { title: "Delaware", value: "USA-DE", country: "USA" },
+ { title: "Florida", value: "USA-FL", country: "USA" },
+ { title: "Georgia", value: "USA-GA", country: "USA" },
+ { title: "Hawaii", value: "USA-HI", country: "USA" },
+ { title: "Idaho", value: "USA-ID", country: "USA" },
+ { title: "Illinois", value: "USA-IL", country: "USA" },
+ { title: "Indiana", value: "USA-IN", country: "USA" },
+ { title: "Iowa", value: "USA-IA", country: "USA" },
+ { title: "Kansas", value: "USA-KS", country: "USA" },
+ { title: "Kentucky", value: "USA-KY", country: "USA" },
+ { title: "Louisiana", value: "USA-LA", country: "USA" },
+ { title: "Maine", value: "USA-ME", country: "USA" },
+ { title: "Maryland", value: "USA-MD", country: "USA" },
+ { title: "Massachusetts", value: "USA-MA", country: "USA" },
+ { title: "Michigan", value: "USA-MI", country: "USA" },
+ { title: "Minnesota", value: "USA-MN", country: "USA" },
+ { title: "Mississippi", value: "USA-MS", country: "USA" },
+ { title: "Missouri", value: "USA-MO", country: "USA" },
+ { title: "Montana", value: "USA-MT", country: "USA" },
+ { title: "Nebraska", value: "USA-NE", country: "USA" },
+ { title: "Nevada", value: "USA-NV", country: "USA" },
+ { title: "New Hampshire", value: "USA-NH", country: "USA" },
+ { title: "New Jersey", value: "USA-NJ", country: "USA" },
+ { title: "New Mexico", value: "USA-NM", country: "USA" },
+ { title: "New York", value: "USA-NY", country: "USA" },
+ { title: "North Carolina", value: "USA-NC", country: "USA" },
+ { title: "North Dakota", value: "USA-ND", country: "USA" },
+ { title: "Ohio", value: "USA-OH", country: "USA" },
+ { title: "Oklahoma", value: "USA-OK", country: "USA" },
+ { title: "Oregon", value: "USA-OR", country: "USA" },
+ { title: "Pennsylvania", value: "USA-PA", country: "USA" },
+ { title: "Rhode Island", value: "USA-RI", country: "USA" },
+ { title: "South Carolina", value: "USA-SC", country: "USA" },
+ { title: "South Dakota", value: "USA-SD", country: "USA" },
+ { title: "Tennessee", value: "USA-TN", country: "USA" },
+ { title: "Texas", value: "USA-TX", country: "USA" },
+ { title: "Utah", value: "USA-UT", country: "USA" },
+ { title: "Vermont", value: "USA-VT", country: "USA" },
+ { title: "Virginia", value: "USA-VA", country: "USA" },
+ { title: "Washington", value: "USA-WA", country: "USA" },
+ { title: "West Virginia", value: "USA-WV", country: "USA" },
+ { title: "Wisconsin", value: "USA-WI", country: "USA" },
+ { title: "Wyoming", value: "USA-WY", country: "USA" },
+ { title: "District of Columbia", value: "USA-DC", country: "USA" },
+ { title: "Alberta", value: "CA-AB", country: "CAN" },
+ { title: "British Columbia", value: "CA-BC", country: "CAN" },
+ { title: "Manitoba", value: "CA-MB", country: "CAN" },
+ { title: "New Brunswick", value: "CA-NB", country: "CAN" },
+ { title: "Newfoundland and Labrador", value: "CA-NL", country: "CAN" },
+ { title: "Northwest Territories", value: "CA-NT", country: "CAN" },
+ { title: "Nova Scotia", value: "CA-NS", country: "CAN" },
+ { title: "Nunavut", value: "CA-NU", country: "CAN" },
+ { title: "Ontario", value: "CA-ON", country: "CAN" },
+ { title: "Prince Edward Island", value: "CA-PE", country: "CAN" },
+ { title: "Quebec", value: "CA-QC", country: "CAN" },
+ { title: "Saskatchewan", value: "CA-SK", country: "CAN" },
+ { title: "Yukon", value: "CA-YT", country: "CAN" },
+ { title: "England", value: "GBR-ENG", country: "GBR" },
+ { title: "Scotland", value: "GBR-SCT", country: "GBR" },
+ { title: "Wales", value: "GBR-WLS", country: "GBR" },
+ { title: "Northern Ireland", value: "GBR-NIR", country: "GBR" },
+ { title: "Andaman and Nicobar Islands", value: "IN-AN", country: "IND" },
+ { title: "Andhra Pradesh", value: "IN-AP", country: "IND" },
+ { title: "Arunachal Pradesh", value: "IN-AR", country: "IND" },
+ { title: "Assam", value: "IN-AS", country: "IND" },
+ { title: "Bihar", value: "IN-BR", country: "IND" },
+ { title: "Chandigarh", value: "IN-CH", country: "IND" },
+ { title: "Chhattisgarh", value: "IN-CG", country: "IND" },
+ { title: "Dadra and Nagar Haveli", value: "IN-DN", country: "IND" },
+ { title: "Daman and Diu", value: "IN-DD", country: "IND" },
+ { title: "Delhi", value: "IN-DL", country: "IND" },
+ { title: "Goa", value: "IN-GA", country: "IND" },
+ { title: "Gujarat", value: "IN-GJ", country: "IND" },
+ { title: "Haryana", value: "IN-HR", country: "IND" },
+ { title: "Himachal Pradesh", value: "IN-HP", country: "IND" },
+ { title: "Jammu and Kashmir", value: "IN-JK", country: "IND" },
+ { title: "Jharkhand", value: "IN-JH", country: "IND" },
+ { title: "Karnataka", value: "IN-KA", country: "IND" },
+ { title: "Kerala", value: "IN-KL", country: "IND" },
+ { title: "Ladakh", value: "IN-LA", country: "IND" },
+ { title: "Lakshadweep", value: "IN-LD", country: "IND" },
+ { title: "Madhya Pradesh", value: "IN-MP", country: "IND" },
+ { title: "Maharashtra", value: "IN-MH", country: "IND" },
+ { title: "Manipur", value: "IN-MN", country: "IND" },
+ { title: "Meghalaya", value: "IN-ML", country: "IND" },
+ { title: "Mizoram", value: "IN-MZ", country: "IND" },
+ { title: "Nagaland", value: "IN-NL", country: "IND" },
+ { title: "Odisha", value: "IN-OD", country: "IND" },
+ { title: "Puducherry", value: "IN-PY", country: "IND" },
+ { title: "Punjab", value: "IN-PB", country: "IND" },
+ { title: "Rajasthan", value: "IN-RJ", country: "IND" },
+ { title: "Sikkim", value: "IN-SK", country: "IND" },
+ { title: "Tamil Nadu", value: "IN-TN", country: "IND" },
+ { title: "Telangana", value: "IN-TS", country: "IND" },
+ { title: "Tripura", value: "IN-TR", country: "IND" },
+ { title: "Uttar Pradesh", value: "IN-UP", country: "IND" },
+ { title: "Uttarakhand", value: "IN-UK", country: "IND" },
+ { title: "West Bengal", value: "IN-WB", country: "IND" },
+ { title: "Australian Capital Territory", value: "AU-ACT", country: "AUS" },
+ { title: "New South Wales", value: "AU-NSW", country: "AUS" },
+ { title: "Northern Territory", value: "AU-NT", country: "AUS" },
+ { title: "Queensland", value: "AU-QLD", country: "AUS" },
+ { title: "South Australia", value: "AU-SA", country: "AUS" },
+ { title: "Tasmania", value: "AU-TAS", country: "AUS" },
+ { title: "Victoria", value: "AU-VIC", country: "AUS" },
+ { title: "Western Australia", value: "AU-WA", country: "AUS" }
+ ]
+
+ # Detect if user wants to view contacts only (get/view/show/list)
+ - kind: SetVariable
+ id: set_is_view_only
+ variable: Topic.isViewOnly
+ value: =(!IsBlank(Topic.InputAction) && "get" in Lower(Topic.InputAction)) || "get" in Lower(System.Activity.Text) || "view" in Lower(System.Activity.Text) || "show" in Lower(System.Activity.Text) || "list" in Lower(System.Activity.Text) || "see my" in Lower(System.Activity.Text) || "what are" in Lower(System.Activity.Text) || "display" in Lower(System.Activity.Text)
+
+ # Detect if user wants to delete a contact
+ - kind: SetVariable
+ id: set_is_delete_mode
+ variable: Topic.isDeleteIntent
+ value: =(!IsBlank(Topic.InputAction) && "delete" in Lower(Topic.InputAction)) || "delete" in Lower(System.Activity.Text) || "remove" in Lower(System.Activity.Text)
+
+ # Check if user explicitly wants to add a new contact - skip loading existing contacts
+ - kind: ConditionGroup
+ id: check_direct_add
+ conditions:
+ - id: user_wants_add_directly
+ condition: =Topic.isViewOnly <> true && ((!IsBlank(Topic.InputAction) && "add" in Lower(Topic.InputAction)) || "add" in Lower(System.Activity.Text) || "new emergency contact" in Lower(System.Activity.Text) || "create" in Lower(System.Activity.Text))
+ displayName: User wants to add a new contact directly
+ actions:
+ - kind: SetVariable
+ id: set_add_mode_direct
+ variable: Topic.isUpdateMode
+ value: =false
+ - kind: SendActivity
+ id: direct_add_msg
+ activity: Sure, I can help you add a new emergency contact.
+ - kind: GotoAction
+ id: goto_country_selection_direct
+ actionId: country_selection_card
+
+ - kind: BeginDialog
+ id: fetch_emergency_contacts
+ displayName: Fetch existing emergency contacts
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetEmergencyContactInfo
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.fetchErrorResponse
+ isSuccess: Topic.fetchIsSuccess
+ workdayResponse: Topic.existingContactsResponse
+
+ - kind: ParseValue
+ id: parse_contacts
+ displayName: Parse emergency contacts response
+ variable: Topic.parsedContacts
+ valueType:
+ kind: Record
+ properties:
+ EmergencyContacts:
+ type:
+ kind: Table
+ properties:
+ Emergency_Contact:
+ type:
+ kind: Record
+ properties:
+ Emergency_Contact_Data:
+ type:
+ kind: Record
+ properties:
+ @Primary: String
+ @Priority: String
+ Emergency_Contact_ID: String
+
+ Emergency_Contact_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Person_Reference:
+ type:
+ kind: Record
+ properties:
+ @Descriptor: String
+
+ Personal_Data:
+ type:
+ kind: Record
+ properties:
+ Contact_Data:
+ type:
+ kind: Record
+ properties:
+ Address_Data:
+ type:
+ kind: Table
+ properties:
+ @Formatted_Address: String
+ Address_Line_Data:
+ type:
+ kind: Record
+ properties:
+ "#text": String
+
+ Country_Reference:
+ type:
+ kind: Record
+ properties:
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Country_Region_Reference:
+ type:
+ kind: Record
+ properties:
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ Municipality: String
+ Postal_Code: String
+
+ Phone_Data:
+ type:
+ kind: Record
+ properties:
+ @Tenant_Formatted_Phone: String
+ International_Phone_Code: String
+ Phone_Number: String
+
+ Name_Data:
+ type:
+ kind: Record
+ properties:
+ Legal_Name_Data:
+ type:
+ kind: Record
+ properties:
+ Name_Detail_Data:
+ type:
+ kind: Record
+ properties:
+ @Formatted_Name: String
+ First_Name: String
+ Last_Name: String
+
+ Related_Person_Relationship_Reference:
+ type:
+ kind: Table
+ properties:
+ @Descriptor: String
+ ID:
+ type:
+ kind: Table
+ properties:
+ @type: String
+ "#text": String
+
+ value: =Topic.existingContactsResponse
+
+ - kind: SetVariable
+ id: set_contact_list
+ displayName: Transform contacts for display
+ variable: Topic.contactSelectionList
+ value: |
+ =ForAll(
+ Filter(
+ Topic.parsedContacts.EmergencyContacts,
+ !IsBlank(LookUp(ThisRecord.Emergency_Contact.Emergency_Contact_Reference.ID, '@type' = "WID").'#text')
+ ),
+ {
+ DisplayName: ThisRecord.Personal_Data.Name_Data.Legal_Name_Data.Name_Detail_Data.'@Formatted_Name',
+ FirstName: ThisRecord.Personal_Data.Name_Data.Legal_Name_Data.Name_Detail_Data.First_Name,
+ LastName: ThisRecord.Personal_Data.Name_Data.Legal_Name_Data.Name_Detail_Data.Last_Name,
+ Phone: ThisRecord.Personal_Data.Contact_Data.Phone_Data.'@Tenant_Formatted_Phone',
+ PhoneNumber: ThisRecord.Personal_Data.Contact_Data.Phone_Data.Phone_Number,
+ PhoneCountryCode: ThisRecord.Personal_Data.Contact_Data.Phone_Data.International_Phone_Code,
+ Address: First(ThisRecord.Personal_Data.Contact_Data.Address_Data).'@Formatted_Address',
+ AddressLine1: First(ThisRecord.Personal_Data.Contact_Data.Address_Data).Address_Line_Data.'#text',
+ City: First(ThisRecord.Personal_Data.Contact_Data.Address_Data).Municipality,
+ PostalCode: First(ThisRecord.Personal_Data.Contact_Data.Address_Data).Postal_Code,
+ StateProvince: LookUp(First(ThisRecord.Personal_Data.Contact_Data.Address_Data).Country_Region_Reference.ID, '@type' = "Country_Region_ID").'#text',
+ CountryCode: LookUp(First(ThisRecord.Personal_Data.Contact_Data.Address_Data).Country_Reference.ID, '@type' = "ISO_3166-1_Alpha-3_Code").'#text',
+ RelationshipType: First(ThisRecord.Related_Person_Relationship_Reference).'@Descriptor',
+ RelationshipTypeID: LookUp(First(ThisRecord.Related_Person_Relationship_Reference).ID, '@type' = "Related_Person_Relationship_ID").'#text',
+ IsPrimary: ThisRecord.Emergency_Contact.Emergency_Contact_Data.'@Primary',
+ Priority: ThisRecord.Emergency_Contact.Emergency_Contact_Data.'@Priority',
+ WID: LookUp(ThisRecord.Emergency_Contact.Emergency_Contact_Reference.ID, '@type' = "WID").'#text'
+ }
+ )
+
+ - kind: ConditionGroup
+ id: check_contacts_exist
+ conditions:
+ - id: has_existing_contacts
+ condition: =CountRows(Topic.contactSelectionList) > 0
+ displayName: If user has existing emergency contacts
+ actions:
+ - kind: SetVariable
+ id: build_selection_choices
+ displayName: Build selection choices
+ variable: Topic.selectionChoices
+ value: |
+ =ForAll(
+ Filter(
+ SortByColumns(Topic.contactSelectionList, "IsPrimary", SortOrder.Descending, "Priority", SortOrder.Ascending),
+ !IsBlank(ThisRecord.DisplayName) && !IsBlank(ThisRecord.WID)
+ ),
+ {
+ title: If(IsBlank(ThisRecord.DisplayName), "Unknown Contact", ThisRecord.DisplayName),
+ value: ThisRecord.WID
+ }
+ )
+
+ - kind: SetVariable
+ id: set_workday_url_2
+ variable: Topic.WorkdayUrl
+ value: https://impl.workday.com//home.htmld
+
+ - kind: SetVariable
+ id: build_contact_table
+ displayName: Build formatted contact table
+ variable: Topic.contactTableText
+ value: |
+ ="| Name | Relationship | Phone | Address |" & Char(10) & "|------|-------------|-------|---------|" & Char(10) & Concat(
+ SortByColumns(Topic.contactSelectionList, "IsPrimary", SortOrder.Descending, "Priority", SortOrder.Ascending),
+ "| " & If(!IsBlank(ThisRecord.DisplayName), ThisRecord.DisplayName, "Unknown") & If(ThisRecord.IsPrimary = "true" || ThisRecord.IsPrimary = "1", " ★", "") & " | " & If(!IsBlank(ThisRecord.RelationshipType), ThisRecord.RelationshipType, "—") & " | " & If(!IsBlank(ThisRecord.Phone), ThisRecord.Phone, "—") & " | " & If(!IsBlank(ThisRecord.Address), ThisRecord.Address, "—") & " |",
+ Char(10)
+ )
+
+ # View-only mode: show table as Adaptive Card (full width) and end
+ - kind: ConditionGroup
+ id: check_view_only
+ conditions:
+ - id: is_view_only_mode
+ condition: =Topic.isViewOnly = true
+ displayName: User only wants to view contacts
+ actions:
+ - kind: SetVariable
+ id: set_view_intro_text
+ variable: Topic.viewIntroText
+ value: ="Here are your emergency contacts from [Workday](" & Topic.WorkdayUrl & "), an HR platform your company uses."
+
+ - kind: SendActivity
+ id: view_only_intro
+ activity: "{Topic.viewIntroText}"
+
+ - kind: SendActivity
+ id: view_only_table_card
+ activity:
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.3",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Your emergency contacts (" & Text(CountRows(Topic.contactSelectionList)) & ")",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true
+ },
+ {
+ type: "ColumnSet",
+ separator: true,
+ spacing: "Medium",
+ columns: [
+ { type: "Column", width: 2, items: [{ type: "TextBlock", text: "Name", weight: "Bolder", wrap: true }] },
+ { type: "Column", width: 2, items: [{ type: "TextBlock", text: "Relationship", weight: "Bolder", wrap: true }] },
+ { type: "Column", width: 2, items: [{ type: "TextBlock", text: "Phone", weight: "Bolder", wrap: true }] },
+ { type: "Column", width: 3, items: [{ type: "TextBlock", text: "Address", weight: "Bolder", wrap: true }] }
+ ]
+ },
+ {
+ type: "Container",
+ items: ForAll(
+ SortByColumns(Topic.contactSelectionList, "IsPrimary", SortOrder.Descending, "Priority", SortOrder.Ascending),
+ {
+ type: "ColumnSet",
+ separator: true,
+ columns: [
+ { type: "Column", width: 2, items: [{ type: "TextBlock", text: If(!IsBlank(ThisRecord.DisplayName), ThisRecord.DisplayName, "Unknown") & If(ThisRecord.IsPrimary = "true" || ThisRecord.IsPrimary = "1", " ★", ""), wrap: true }] },
+ { type: "Column", width: 2, items: [{ type: "TextBlock", text: If(!IsBlank(ThisRecord.RelationshipType), ThisRecord.RelationshipType, "—"), wrap: true }] },
+ { type: "Column", width: 2, items: [{ type: "TextBlock", text: If(!IsBlank(ThisRecord.Phone), ThisRecord.Phone, "—"), wrap: true }] },
+ { type: "Column", width: 3, items: [{ type: "TextBlock", text: If(!IsBlank(ThisRecord.Address), ThisRecord.Address, "—"), wrap: true }] }
+ ]
+ }
+ )
+ }
+ ]
+ }
+
+ - kind: SendActivity
+ id: view_only_footer
+ activity: "If you need to update or add any information for these contacts, just let me know which one you'd like to change or if you want to add a new contact."
+
+ - kind: CancelAllDialogs
+ id: end_view_only
+
+ - kind: SetVariable
+ id: set_intro_msg_text
+ variable: Topic.introMsgText
+ value: ="Sure, I can help you with that. Here's where you can update and add emergency contacts. I've identified " & Text(CountRows(Topic.selectionChoices)) & " emergency contact(s) of yours from [Workday](" & Topic.WorkdayUrl & "), an HR platform your company uses."
+
+ - kind: SendActivity
+ id: intro_selection_msg
+ activity: "{Topic.introMsgText}"
+
+ - kind: AdaptiveCardPrompt
+ id: select_contact_card
+ displayName: Select emergency contact to update
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: If(Topic.isDeleteIntent, "Select an emergency contact to delete", "Select an emergency contact to update, delete, or add a new contact."),
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "TextBlock",
+ text: If(Topic.isDeleteIntent, "You have " & CountRows(Topic.selectionChoices) & " emergency contact(s). Select the contact you want to delete.", "You have " & CountRows(Topic.selectionChoices) & " emergency contact(s). Select to update or add a new contact."),
+ wrap: true,
+ spacing: "Small"
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "selectedContact",
+ label: "Emergency contact",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select an emergency contact.",
+ choices: ForAll(Topic.selectionChoices, { title: ThisRecord.title, value: ThisRecord.value }),
+ value: First(Topic.selectionChoices).value
+ }
+ ],
+ actions: If(Topic.isDeleteIntent,
+ [
+ { type: "Action.Submit", title: "Delete contact", id: "DELETE", data: { actionSubmitId: "DELETE" } },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", data: { actionSubmitId: "Cancel" }, associatedInputs: "none" }
+ ],
+ [
+ { type: "Action.Submit", title: "Continue", id: "Continue", data: { actionSubmitId: "Continue" } },
+ { type: "Action.Submit", title: "Delete contact", id: "DELETE", data: { actionSubmitId: "DELETE" } },
+ { type: "Action.Submit", title: "Add an emergency contact", id: "ADD_NEW", data: { actionSubmitId: "ADD_NEW" }, associatedInputs: "none" }
+ ]
+ )
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.selectionActionId
+ selectedContact: Topic.selectedContactWID
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ selectedContact: String
+
+ - kind: ConditionGroup
+ id: handle_selection_cancel
+ conditions:
+ - id: selection_cancelled
+ condition: =Topic.selectionActionId = "Cancel"
+ actions:
+ - kind: SendActivity
+ id: selection_cancel_msg
+ activity: Your request has been cancelled. Is there anything else you need help with?
+
+ - kind: CancelAllDialogs
+ id: selection_cancel_all
+
+ - kind: ConditionGroup
+ id: set_mode
+ conditions:
+ - id: is_update_mode
+ condition: =Topic.selectionActionId = "Continue"
+ displayName: Update existing contact
+ actions:
+ - kind: SetVariable
+ id: set_update_mode
+ variable: Topic.isUpdateMode
+ value: =true
+
+ - kind: SetVariable
+ id: set_selected_contact_data
+ variable: Topic.selectedContactData
+ value: =LookUp(Topic.contactSelectionList, WID = Topic.selectedContactWID)
+
+ - kind: ConditionGroup
+ id: handle_selection_delete
+ conditions:
+ - id: selection_delete_requested
+ condition: =Topic.selectionActionId = "DELETE"
+ displayName: User wants to delete from selection card
+ actions:
+ - kind: SetVariable
+ id: set_selected_contact_for_delete
+ variable: Topic.selectedContactData
+ value: =LookUp(Topic.contactSelectionList, WID = Topic.selectedContactWID)
+
+ - kind: GotoAction
+ id: goto_delete_confirmation
+ actionId: handle_delete_action
+
+ elseActions:
+ - kind: SetVariable
+ id: set_add_mode
+ variable: Topic.isUpdateMode
+ value: =false
+
+ elseActions:
+ - kind: SetVariable
+ id: set_add_mode_no_contacts
+ variable: Topic.isUpdateMode
+ value: =false
+
+ - kind: SendActivity
+ id: no_contacts_msg
+ activity: You don't have any emergency contacts configured yet. Let's add one now.
+
+ # Show context message before form (only for update mode)
+ - kind: ConditionGroup
+ id: show_update_context_msg
+ conditions:
+ - id: is_update_show_msg
+ condition: =Topic.isUpdateMode = true
+ actions:
+ - kind: SetVariable
+ id: set_update_context_text
+ variable: Topic.updateContextText
+ value: ="You've pulled up " & Topic.selectedContactData.FirstName & " " & Topic.selectedContactData.LastName & "'s emergency contact details. In this form you can edit details or remove " & Topic.selectedContactData.FirstName & " from your emergency contacts."
+
+ - kind: SendActivity
+ id: update_context_msg
+ activity: "{Topic.updateContextText}"
+
+ # Step 1: Country selection card (needed to filter states in the next card)
+ - kind: AdaptiveCardPrompt
+ id: country_selection_card
+ displayName: Select country for address
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Select country",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "TextBlock",
+ text: "Choose the country for this contact's address. The state/province options in the next step will be filtered accordingly.",
+ wrap: true,
+ size: "Small",
+ spacing: "Small"
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "countryCode",
+ label: "Country",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a country.",
+ choices: [
+ { title: "United States", value: "USA" },
+ { title: "Canada", value: "CAN" },
+ { title: "United Kingdom", value: "GBR" },
+ { title: "India", value: "IND" },
+ { title: "Australia", value: "AUS" }
+ ],
+ value: If(Topic.isUpdateMode, Topic.selectedContactData.CountryCode, "USA")
+ }
+ ],
+ actions: [
+ { type: "Action.Submit", title: "Continue", id: "Continue", data: { actionSubmitId: "Continue" } },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", data: { actionSubmitId: "Cancel" }, associatedInputs: "none" }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.countrySelectionActionId
+ countryCode: Topic.countryCode
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ countryCode: String
+
+ - kind: ConditionGroup
+ id: handle_country_cancel
+ conditions:
+ - id: country_cancelled
+ condition: =Topic.countrySelectionActionId = "Cancel"
+ actions:
+ - kind: SendActivity
+ id: country_cancel_msg
+ activity: Your request has been cancelled. Is there anything else you need help with?
+
+ - kind: CancelAllDialogs
+ id: country_cancel_all
+
+ # Step 2: Build filtered state/province choices based on selected country
+ - kind: SetVariable
+ id: set_filtered_state_choices
+ displayName: Filter states by selected country
+ variable: Topic.filteredStateProvinceChoices
+ value: =Filter(Global.StateProvinceChoices, country = Topic.countryCode)
+
+ # Map address country to default phone dial code
+ - kind: SetVariable
+ id: set_default_phone_dial_code
+ displayName: Default phone dial code for selected country
+ variable: Topic.defaultPhoneDialCode
+ value: =Switch(Topic.countryCode, "USA", "1", "CAN", "1", "GBR", "44", "IND", "91", "AUS", "61", "1")
+
+ # Step 3: Main emergency contact form (with filtered states)
+ - kind: AdaptiveCardPrompt
+ id: emergency_contact_form
+ displayName: Emergency contact form
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: If(Topic.isUpdateMode, "Update emergency contact", "Add emergency contact"),
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "TextBlock",
+ text: "Fields marked with * are required.",
+ wrap: true,
+ size: "Small",
+ spacing: "Small"
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "firstName",
+ label: "First name",
+ placeholder: "Enter first name",
+ isRequired: true,
+ errorMessage: "First name is required.",
+ maxLength: 100,
+ value: If(Topic.isUpdateMode, Topic.selectedContactData.FirstName, "")
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "lastName",
+ label: "Last name",
+ placeholder: "Enter last name",
+ isRequired: true,
+ errorMessage: "Last name is required.",
+ maxLength: 100,
+ value: If(Topic.isUpdateMode, Topic.selectedContactData.LastName, "")
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "priority",
+ label: "Priority (only applies if not primary)",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a priority.",
+ choices: [
+ { title: "2 - High", value: "2" },
+ { title: "3", value: "3" },
+ { title: "4", value: "4" },
+ { title: "5 - Medium", value: "5" },
+ { title: "6", value: "6" },
+ { title: "7", value: "7" },
+ { title: "8", value: "8" },
+ { title: "9", value: "9" },
+ { title: "10 - Low", value: "10" }
+ ],
+ value: If(Topic.isUpdateMode, If(Value(Topic.selectedContactData.Priority) > 1, Topic.selectedContactData.Priority, "2"), "2")
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "relationshipType",
+ label: "Relationship",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a relationship type.",
+ choices: ForAll(Global.RelatedPersonRelationshipLookupTable, { title: ThisRecord.Referenced_Object_Descriptor, value: ThisRecord.ID }),
+ value: If(Topic.isUpdateMode, Topic.selectedContactData.RelationshipTypeID, First(Global.RelatedPersonRelationshipLookupTable).ID)
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "Input.Toggle",
+ id: "isPrimaryContact",
+ title: "Set as primary emergency contact",
+ value: If(Topic.isUpdateMode, If(Topic.selectedContactData.IsPrimary = "true" || Topic.selectedContactData.IsPrimary = "1", "true", "false"), "false"),
+ valueOn: "true",
+ valueOff: "false"
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "phoneDeviceType",
+ label: "Phone type",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a phone type.",
+ separator: true,
+ choices: [
+ { title: "Mobile", value: "Mobile" },
+ { title: "Home", value: "Home" },
+ { title: "Work", value: "Work" }
+ ],
+ value: "Mobile",
+ spacing: "Medium"
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "phoneCountryCode",
+ label: "Phone country code",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a country code.",
+ choices: ForAll(Global.CountryCodeLookupTable, { title: ThisRecord.Referenced_Object_Descriptor, value: ThisRecord.ID }),
+ value: If(Topic.isUpdateMode, LookUp(Global.CountryCodeLookupTable, Last(Split(ID, "_")).Value = Topic.selectedContactData.PhoneCountryCode).ID, LookUp(Global.CountryCodeLookupTable, Last(Split(ID, "_")).Value = Topic.defaultPhoneDialCode).ID)
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "phoneNumber",
+ label: "Phone number",
+ placeholder: "Enter phone number",
+ isRequired: true,
+ errorMessage: "Phone number is required.",
+ maxLength: 20,
+ value: If(Topic.isUpdateMode, Topic.selectedContactData.PhoneNumber, "")
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "Input.Text",
+ id: "addressLine1",
+ label: "Address line 1",
+ placeholder: "Enter street address",
+ isRequired: true,
+ errorMessage: "Address is required.",
+ maxLength: 200,
+ value: If(Topic.isUpdateMode, Topic.selectedContactData.AddressLine1, ""),
+ separator: true,
+ spacing: "Medium"
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "city",
+ label: "City",
+ placeholder: "Enter city",
+ isRequired: true,
+ errorMessage: "City is required.",
+ maxLength: 100,
+ value: If(Topic.isUpdateMode, Topic.selectedContactData.City, "")
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "stateProvince",
+ label: "State/Province",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a state/province.",
+ choices: If(CountRows(Topic.filteredStateProvinceChoices) > 0, ForAll(Topic.filteredStateProvinceChoices, { title: ThisRecord.title, value: ThisRecord.value }), [{ title: "N/A — enter details in Address field", value: "N_A" }]),
+ value: If(Topic.isUpdateMode && !IsBlank(Topic.selectedContactData.StateProvince), Topic.selectedContactData.StateProvince, If(CountRows(Topic.filteredStateProvinceChoices) > 0, First(Topic.filteredStateProvinceChoices).value, "N_A"))
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "postalCode",
+ label: "Postal code",
+ placeholder: "Enter postal code",
+ isRequired: true,
+ errorMessage: "Postal code is required.",
+ maxLength: 20,
+ value: If(Topic.isUpdateMode, Topic.selectedContactData.PostalCode, "")
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "TextBlock",
+ text: "Country",
+ size: "Small",
+ weight: "Bolder",
+ spacing: "Small"
+ },
+ {
+ type: "TextBlock",
+ text: LookUp([{v:"USA",t:"United States"},{v:"CAN",t:"Canada"},{v:"GBR",t:"United Kingdom"},{v:"IND",t:"India"},{v:"AUS",t:"Australia"}], v = Topic.countryCode).t,
+ wrap: true,
+ spacing: "Small"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ actions: If(Topic.isUpdateMode,
+ [
+ { type: "Action.Submit", title: "Submit changes", id: "Submit", data: { actionSubmitId: "Submit" } },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", data: { actionSubmitId: "Cancel" }, associatedInputs: "none" }
+ ],
+ [
+ { type: "Action.Submit", title: "Submit", id: "Submit", data: { actionSubmitId: "Submit" } },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", data: { actionSubmitId: "Cancel" }, associatedInputs: "none" }
+ ]
+ )
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.formActionId
+ addressLine1: Topic.addressLine1
+ city: Topic.city
+ firstName: Topic.firstName
+ isPrimaryContact: Topic.isPrimaryContact
+ lastName: Topic.lastName
+ phoneCountryCode: Topic.phoneCountryCode
+ phoneDeviceType: Topic.phoneDeviceType
+ phoneNumber: Topic.phoneNumber
+ postalCode: Topic.postalCode
+ priority: Topic.priority
+ relationshipType: Topic.relationshipType
+ stateProvince: Topic.stateProvince
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ addressLine1: String
+ city: String
+ firstName: String
+ isPrimaryContact: String
+ lastName: String
+ phoneCountryCode: String
+ phoneDeviceType: String
+ phoneNumber: String
+ postalCode: String
+ priority: String
+ relationshipType: String
+ stateProvince: String
+
+ - kind: ConditionGroup
+ id: handle_form_cancel
+ conditions:
+ - id: form_cancelled
+ condition: =Topic.formActionId = "Cancel"
+ actions:
+ - kind: SendActivity
+ id: form_cancel_msg
+ activity: Your request has been cancelled. Is there anything else you need help with?
+
+ - kind: CancelAllDialogs
+ id: form_cancel_all
+
+ # Handle delete action - show confirmation (reached from selection card or form)
+ - kind: ConditionGroup
+ id: handle_delete_action
+ conditions:
+ - id: delete_requested
+ condition: =Topic.formActionId = "Delete" || Topic.selectionActionId = "DELETE"
+ displayName: User wants to delete contact
+ actions:
+ # Check if contact is primary - don't allow delete
+ - kind: ConditionGroup
+ id: check_primary_delete
+ conditions:
+ - id: is_primary_contact
+ condition: =Topic.selectedContactData.IsPrimary = "true" || Topic.selectedContactData.IsPrimary = "1"
+ displayName: Cannot delete primary contact
+ actions:
+ - kind: SendActivity
+ id: cannot_delete_primary_msg
+ activity: You cannot delete your primary emergency contact. Please designate another contact as primary first, then you can remove this one.
+
+ - kind: CancelAllDialogs
+ id: cancel_primary_delete
+
+ elseActions:
+ # Not primary - proceed with delete confirmation
+ - kind: SetVariable
+ id: set_delete_confirm_text
+ variable: Topic.deleteConfirmText
+ value: ="Are you sure you want to delete this emergency contact?"
+
+ - kind: SendActivity
+ id: delete_confirm_text_msg
+ activity: "{Topic.deleteConfirmText}"
+
+ - kind: AdaptiveCardPrompt
+ id: confirm_delete_card
+ displayName: Confirm delete
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Confirm deletion",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "FactSet",
+ spacing: "Medium",
+ facts: [
+ { title: "Name", value: Topic.selectedContactData.FirstName & " " & Topic.selectedContactData.LastName },
+ { title: "Priority", value: LookUp([{v:"2",t:"2 - High"},{v:"3",t:"3"},{v:"4",t:"4"},{v:"5",t:"5 - Medium"},{v:"6",t:"6"},{v:"7",t:"7"},{v:"8",t:"8"},{v:"9",t:"9"},{v:"10",t:"10 - Low"}], v = Topic.selectedContactData.Priority).t },
+ { title: "Relationship", value: Topic.selectedContactData.RelationshipType },
+ { title: "Phone", value: Topic.selectedContactData.Phone },
+ { title: "Address", value: Topic.selectedContactData.Address }
+ ]
+ }
+ ],
+ actions: [
+ { type: "Action.Submit", title: "Yes, delete", id: "Yes", data: { actionSubmitId: "Yes" } },
+ { type: "Action.Submit", title: "Cancel", id: "No", data: { actionSubmitId: "No" } }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.confirmDeleteAction
+
+ outputType:
+ properties:
+ actionSubmitId: String
+
+ - kind: ConditionGroup
+ id: check_delete_confirmation
+ conditions:
+ - id: confirmed_delete
+ condition: =Topic.confirmDeleteAction = "Yes"
+ displayName: User confirmed delete
+ actions:
+ - kind: BeginDialog
+ id: execute_delete
+ displayName: Execute Workday Delete
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{Emergency_Contact_WID}"",""value"":""" & Topic.selectedContactWID & """},{""key"":""{Priority}"",""value"":""" & Topic.selectedContactData.Priority & """},{""key"":""{Relationship_Type}"",""value"":""" & Topic.selectedContactData.RelationshipTypeID & """},{""key"":""{Comment}"",""value"":""Removing emergency contact via Copilot""}]}"
+ scenarioName: msdyn_DeleteEmergencyContact
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ConditionGroup
+ id: report_delete_result
+ conditions:
+ - id: delete_failed
+ condition: =Topic.isSuccess = false
+ actions:
+ # Parse error message for display
+ - kind: SetVariable
+ id: set_delete_error_raw
+ variable: Topic.deleteErrorRaw
+ value: =Topic.errorResponse
+
+ - kind: SetVariable
+ id: parse_delete_error
+ variable: Topic.deleteErrorParsed
+ value: =IfError(Text(ParseJSON(Topic.deleteErrorRaw).error.message), IfError(Text(ParseJSON(Topic.deleteErrorRaw).message), Topic.deleteErrorRaw))
+
+ - kind: SetVariable
+ id: set_delete_error_display
+ variable: Topic.deleteErrorDisplay
+ value: =If(IsBlank(Topic.deleteErrorParsed), "An unknown error occurred.", Topic.deleteErrorParsed)
+
+ - kind: SendActivity
+ id: delete_failure_msg
+ activity: |-
+ An error occurred and the emergency contact was not deleted.
+
+ **Error details:** {Topic.deleteErrorDisplay}
+
+ Please try again or contact support.
+
+ - kind: CancelAllDialogs
+ id: end_on_delete_failure
+
+ elseActions:
+ - kind: SendActivity
+ id: delete_success_msg
+ activity:
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "✅ Emergency contact deleted",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "FactSet",
+ spacing: "Medium",
+ facts: [
+ { title: "Name", value: Topic.selectedContactData.FirstName & " " & Topic.selectedContactData.LastName },
+ { title: "Priority", value: LookUp([{v:"2",t:"2 - High"},{v:"3",t:"3"},{v:"4",t:"4"},{v:"5",t:"5 - Medium"},{v:"6",t:"6"},{v:"7",t:"7"},{v:"8",t:"8"},{v:"9",t:"9"},{v:"10",t:"10 - Low"}], v = Topic.selectedContactData.Priority).t },
+ { title: "Relationship", value: Topic.selectedContactData.RelationshipType },
+ { title: "Phone", value: Topic.selectedContactData.Phone },
+ { title: "Address", value: Topic.selectedContactData.Address }
+ ]
+ }
+ ]
+ }
+
+ - kind: CancelAllDialogs
+ id: end_delete_dialogs
+
+ elseActions:
+ - kind: SendActivity
+ id: delete_cancelled_msg
+ activity: Delete cancelled. Is there anything else I can help you with?
+
+ - kind: CancelAllDialogs
+ id: cancel_delete_dialogs
+
+ # Only process submit action (not cancel or delete)
+ - kind: ConditionGroup
+ id: check_submit_action
+ conditions:
+ - id: is_submit_action
+ condition: =Topic.formActionId = "Submit"
+ displayName: User submitted the form
+ actions:
+ - kind: ConditionGroup
+ id: submit_to_workday
+ conditions:
+ - id: is_update_submit
+ condition: =Topic.isUpdateMode = true
+ displayName: Submit update to Workday
+ actions:
+ - kind: BeginDialog
+ id: execute_update
+ displayName: Execute Workday Update
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{Emergency_Contact_WID}"",""value"":""" & Topic.selectedContactWID & """},{""key"":""{Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""},{""key"":""{First_Name}"",""value"":""" & Topic.firstName & """},{""key"":""{Last_Name}"",""value"":""" & Topic.lastName & """},{""key"":""{Primary}"",""value"":""" & Topic.isPrimaryContact & """},{""key"":""{Priority}"",""value"":""" & If(Topic.isPrimaryContact = "true", "1", Topic.priority) & """},{""key"":""{Relationship_Type}"",""value"":""" & Topic.relationshipType & """},{""key"":""{Phone_Number}"",""value"":""" & Topic.phoneNumber & """},{""key"":""{Phone_Device_Type}"",""value"":""" & Topic.phoneDeviceType & """},{""key"":""{Phone_Country_Code}"",""value"":""" & Last(Split(Topic.phoneCountryCode, "_")).Value & """},{""key"":""{Address_Line_1}"",""value"":""" & Topic.addressLine1 & """},{""key"":""{City}"",""value"":""" & Topic.city & """},{""key"":""{State_Province}"",""value"":""" & Topic.stateProvince & """},{""key"":""{Postal_Code}"",""value"":""" & Topic.postalCode & """},{""key"":""{Country_Code}"",""value"":""" & Topic.countryCode & """},{""key"":""{Address_Country_Code}"",""value"":""" & Topic.countryCode & """},{""key"":""{Comment}"",""value"":""Updating emergency contact""}]}"
+ scenarioName: msdyn_UpdateEmergencyContact
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ elseActions:
+ - kind: BeginDialog
+ id: execute_add
+ displayName: Execute Workday Add
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""},{""key"":""{First_Name}"",""value"":""" & Topic.firstName & """},{""key"":""{Last_Name}"",""value"":""" & Topic.lastName & """},{""key"":""{Primary}"",""value"":""" & Topic.isPrimaryContact & """},{""key"":""{Priority}"",""value"":""" & If(Topic.isPrimaryContact = "true", "1", Topic.priority) & """},{""key"":""{Relationship_Type}"",""value"":""" & Topic.relationshipType & """},{""key"":""{Phone_Number}"",""value"":""" & Topic.phoneNumber & """},{""key"":""{Phone_Device_Type}"",""value"":""" & Topic.phoneDeviceType & """},{""key"":""{Phone_Country_Code}"",""value"":""" & Last(Split(Topic.phoneCountryCode, "_")).Value & """},{""key"":""{Address_Line_1}"",""value"":""" & Topic.addressLine1 & """},{""key"":""{City}"",""value"":""" & Topic.city & """},{""key"":""{State_Province}"",""value"":""" & Topic.stateProvince & """},{""key"":""{Postal_Code}"",""value"":""" & Topic.postalCode & """},{""key"":""{Country_Code}"",""value"":""" & Topic.countryCode & """},{""key"":""{Address_Country_Code}"",""value"":""" & Topic.countryCode & """},{""key"":""{Comment}"",""value"":""Adding emergency contact""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeAddEmergencyContact
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ConditionGroup
+ id: report_result
+ conditions:
+ - id: failed
+ condition: =Topic.isSuccess = false
+ actions:
+ # Parse error message for display
+ - kind: SetVariable
+ id: set_submit_error_raw
+ variable: Topic.submitErrorRaw
+ value: =Topic.errorResponse
+
+ - kind: SetVariable
+ id: parse_submit_error
+ variable: Topic.submitErrorParsed
+ value: =IfError(Text(ParseJSON(Topic.submitErrorRaw).error.message), IfError(Text(ParseJSON(Topic.submitErrorRaw).message), Topic.submitErrorRaw))
+
+ - kind: SetVariable
+ id: set_submit_error_display
+ variable: Topic.submitErrorDisplay
+ value: =If(IsBlank(Topic.submitErrorParsed), "An unknown error occurred.", Topic.submitErrorParsed)
+
+ - kind: SetVariable
+ id: set_failure_msg_text
+ variable: Topic.failureMsgText
+ value: =If(Topic.isUpdateMode, "An error occurred and your emergency contact was not updated.", "An error occurred and your emergency contact was not added.")
+
+ - kind: SendActivity
+ id: failure_msg
+ activity: |-
+ {Topic.failureMsgText}
+
+ **Error details:** {Topic.submitErrorDisplay}
+
+ Please try again or contact support.
+
+ - kind: CancelAllDialogs
+ id: end_on_failure
+
+ elseActions:
+ - kind: SetVariable
+ id: set_workday_url
+ variable: Topic.WorkdayUrl
+ value: ="https://impl.workday.com//home.htmld"
+
+ - kind: SetVariable
+ id: set_success_intro_text
+ variable: Topic.successIntroText
+ value: =If(Topic.isUpdateMode, "Great! You've updated your emergency contact.", "Great! You've added a new emergency contact.")
+
+ - kind: SendActivity
+ id: success_intro_msg
+ activity: "{Topic.successIntroText}"
+
+ - kind: SendActivity
+ id: success_card
+ activity:
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: If(Topic.isUpdateMode, "✅ Emergency contact updated", "✅ Emergency contact added"),
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "FactSet",
+ spacing: "Medium",
+ facts: [
+ { title: "Name", value: Topic.firstName & " " & Topic.lastName },
+ { title: "Priority", value: If(Topic.isPrimaryContact = "true", "Primary", LookUp([{v:"2",t:"2 - High"},{v:"3",t:"3"},{v:"4",t:"4"},{v:"5",t:"5 - Medium"},{v:"6",t:"6"},{v:"7",t:"7"},{v:"8",t:"8"},{v:"9",t:"9"},{v:"10",t:"10 - Low"}], v = Topic.priority).t) },
+ { title: "Relationship", value: LookUp(Global.RelatedPersonRelationshipLookupTable, ID = Topic.relationshipType).Referenced_Object_Descriptor },
+ { title: "Phone", value: "+" & Last(Split(Topic.phoneCountryCode, "_")).Value & "-" & Topic.phoneNumber & " (" & Topic.phoneDeviceType & ")" },
+ { title: "Address", value: Topic.addressLine1 & " " & Topic.city & ", " & Topic.countryCode & " " & Topic.postalCode }
+ ]
+ }
+ ]
+ }
+
+ - kind: SendActivity
+ id: success_footer_msg
+ activity: Is there anything else I can help you with?
+
+ - kind: CancelAllDialogs
+ id: end_dialogs
+
+ elseActions:
+ # This handles case when formActionId is not "Submit" (fallback for Cancel/Delete that weren't caught)
+ - kind: CancelAllDialogs
+ id: end_unexpected_action
+
+inputType:
+ properties:
+ InputAction:
+ displayName: InputAction
+ description: The action the user wants to perform (add, update, manage).
+ type: String
+
+outputType: {}
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/update_emergency_contact.png b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/update_emergency_contact.png
new file mode 100644
index 00000000..a91628fa
Binary files /dev/null and b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/update_emergency_contact.png differ
diff --git a/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/update_emergency_contact_2.png b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/update_emergency_contact_2.png
new file mode 100644
index 00000000..46ae5a9d
Binary files /dev/null and b/EmployeeSelfServiceAgent/Workday/EmployeeScenarios/WorkdayManageEmergencyContact/update_emergency_contact_2.png differ
diff --git a/EmployeeSelfServiceAgent/Workday/ExtendedTopics/GetInboxTasks.yaml b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/GetInboxTasks.yaml
new file mode 100644
index 00000000..2cfcaf80
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/GetInboxTasks.yaml
@@ -0,0 +1,188 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ Shows the employee's open tasks from their Workday inbox.
+ Available fields per task: task title (descriptor), due date, assigned date, step type, initiator (who submitted), overall process name.
+
+ Display rules:
+ - Heading: "Your open tasks in Workday"
+ - Show total count: "You have N open task(s)"
+ - If total exceeds the shown count, note: "showing the most recent N"
+ - List each task with: title, process, who it is from, step type, assigned date, due date
+ - If no tasks: "Your Workday inbox is empty. No open tasks found."
+ - Answer only for the requesting user's own tasks. Do not retrieve tasks for others.
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - What tasks do I have in Workday?
+ - Show my open tasks
+ - What's in my Workday inbox?
+ - Do I have any pending tasks?
+ - What actions are waiting for me in Workday?
+ - Show me my to-do list in Workday
+ - My Workday tasks
+ - Any tasks I need to complete?
+
+ actions:
+ - kind: SetVariable
+ id: set_max_tasks
+ displayName: Set max tasks to show
+ variable: Topic.MaxTasks
+ value: =20
+
+ - kind: BeginDialog
+ id: get_inbox_tasks
+ displayName: Get Workday inbox tasks
+ input:
+ binding:
+ parameters: ="{""workerID"":""Employee_ID=" & Global.ESS_UserContext_Employee_Id & """,""limit"":" & Topic.MaxTasks & ",""offset"":0}"
+ operationName: GetWorkerInboxTasks
+
+ dialog: msdyn_copilotforemployeeselfservice.topic.WorkdaySystemGetRESTExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ConditionGroup
+ id: handle_result
+ conditions:
+ - id: success
+ condition: =Topic.isSuccess = true
+ actions:
+ - kind: ParseValue
+ id: parse_inbox
+ displayName: Parse inbox tasks response
+ variable: Topic.parsedInbox
+ valueType:
+ kind: Record
+ properties:
+ data:
+ type:
+ kind: Table
+ properties:
+ id: String
+ descriptor: String
+ due: String
+ assigned: String
+ stepType:
+ type:
+ kind: Record
+ properties:
+ descriptor: String
+ initiator:
+ type:
+ kind: Record
+ properties:
+ descriptor: String
+ overallProcess:
+ type:
+ kind: Record
+ properties:
+ descriptor: String
+ total:
+ type: Number
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: set_shown_count
+ variable: Topic.ShownCount
+ value: =CountRows(Topic.parsedInbox.data)
+
+ - kind: SetVariable
+ id: set_total
+ variable: Topic.TotalTasks
+ value: =Coalesce(Topic.parsedInbox.total, Topic.ShownCount)
+
+ - kind: ConditionGroup
+ id: check_empty
+ conditions:
+ - id: has_tasks
+ condition: =Topic.ShownCount > 0
+ actions:
+ - kind: SendActivity
+ id: show_tasks_card
+ activity:
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Your open tasks in Workday",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true
+ },
+ {
+ type: "TextBlock",
+ text: "You have " & Topic.TotalTasks & " open task" & If(Topic.TotalTasks = 1, "", "s") & If(Topic.TotalTasks > Topic.ShownCount, ", showing the most recent " & Topic.ShownCount, ""),
+ wrap: true,
+ spacing: "Small",
+ isSubtle: true
+ },
+ {
+ type: "Container",
+ spacing: "Medium",
+ items: ForAll(
+ Topic.parsedInbox.data,
+ {
+ type: "Container",
+ style: "emphasis",
+ bleed: false,
+ spacing: "Small",
+ items: [
+ {
+ type: "TextBlock",
+ text: Coalesce(descriptor, "Untitled task"),
+ weight: "Bolder",
+ wrap: true
+ },
+ {
+ type: "FactSet",
+ spacing: "Small",
+ facts: [
+ { title: "Process", value: If(IsBlank(overallProcess.descriptor), "-", overallProcess.descriptor) },
+ { title: "From", value: If(IsBlank(initiator.descriptor), "-", initiator.descriptor) },
+ { title: "Type", value: If(IsBlank(stepType.descriptor), "-", stepType.descriptor) },
+ { title: "Assigned", value: If(IsBlank(assigned), "-", Left(assigned, 10)) },
+ { title: "Due", value: If(IsBlank(due), "-", Left(due, 10)) }
+ ]
+ }
+ ]
+ }
+ )
+ }
+ ],
+ actions: []
+ }
+
+ - kind: CancelAllDialogs
+ id: end_dialogs
+
+ elseActions:
+ - kind: SendActivity
+ id: no_tasks
+ activity: Your Workday inbox is empty. No open tasks found. Is there anything else I can help with?
+
+ - kind: CancelAllDialogs
+ id: end_no_tasks
+
+ elseActions:
+ - kind: SendActivity
+ id: error_msg
+ activity: I was not able to retrieve your tasks right now. Please try again or check your Workday inbox directly.
+
+ - kind: CancelAllDialogs
+ id: end_error
+
+inputType: {}
+outputType: {}
diff --git a/EmployeeSelfServiceAgent/Workday/ExtendedTopics/GetPaySlips.yaml b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/GetPaySlips.yaml
new file mode 100644
index 00000000..bf624735
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/GetPaySlips.yaml
@@ -0,0 +1,185 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ Shows the employee's pay slip history from Workday.
+ Available fields per pay slip: pay period name (descriptor), pay date, gross pay, net pay, status.
+
+ Display rules:
+ - Heading: "Your pay slips"
+ - Show count: "Here are your N most recent pay slips from Workday."
+ - Each slip shows: pay period name, pay date, gross pay, net pay, status
+ - If no pay slips found: "No pay slips were found in Workday."
+ - Answer only if the user is asking about their own pay slips.
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - Show my pay slips
+ - View my pay stubs
+ - Where can I find my payslip?
+ - Show my pay history
+ - I want to see my paycheck
+ - Download my pay stub
+ - What is my latest paycheck?
+ - Show me my salary slips
+ - Pay slips
+
+ actions:
+ - kind: SetVariable
+ id: set_max_slips
+ displayName: Set max pay slips to fetch
+ variable: Topic.MaxSlips
+ value: =10
+
+ - kind: BeginDialog
+ id: get_pay_slips
+ displayName: Get pay slips
+ input:
+ binding:
+ parameters: ="{""workerID"":""Employee_ID=" & Global.ESS_UserContext_Employee_Id & """,""limit"":" & Topic.MaxSlips & ",""offset"":0}"
+ operationName: GetWorkerPaySlips
+
+ dialog: msdyn_copilotforemployeeselfservice.topic.WorkdaySystemGetRESTExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ConditionGroup
+ id: handle_result
+ conditions:
+ - id: success
+ condition: =Topic.isSuccess = true
+ actions:
+ - kind: ParseValue
+ id: parse_payslips
+ displayName: Parse pay slips response
+ variable: Topic.parsedPaySlips
+ valueType:
+ kind: Record
+ properties:
+ data:
+ type:
+ kind: Table
+ properties:
+ id: String
+ descriptor: String
+ date: String
+ gross: Number
+ net: Number
+ status:
+ type:
+ kind: Record
+ properties:
+ descriptor: String
+ total:
+ type: Number
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: set_count
+ variable: Topic.slipCount
+ value: =CountRows(Topic.parsedPaySlips.data)
+
+ - kind: ConditionGroup
+ id: check_empty
+ conditions:
+ - id: has_slips
+ condition: =Topic.slipCount > 0
+ actions:
+ - kind: SendActivity
+ id: show_slips
+ activity:
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Your pay slips",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true
+ },
+ {
+ type: "TextBlock",
+ text: "Here are your " & Topic.slipCount & " most recent pay slip" & If(Topic.slipCount = 1, "", "s") & " from Workday.",
+ wrap: true,
+ spacing: "Small",
+ isSubtle: true
+ },
+ {
+ type: "Container",
+ spacing: "Medium",
+ items: ForAll(
+ Topic.parsedPaySlips.data,
+ {
+ type: "Container",
+ style: "emphasis",
+ bleed: false,
+ spacing: "Small",
+ items: [
+ {
+ type: "TextBlock",
+ text: Coalesce(descriptor, "Pay slip"),
+ weight: "Bolder",
+ wrap: true
+ },
+ {
+ type: "FactSet",
+ spacing: "Small",
+ facts: [
+ {
+ title: "Pay date",
+ value: If(IsBlank(date), "-", Left(date, 10))
+ },
+ {
+ title: "Gross pay",
+ value: If(IsBlank(Text(gross)), "-", Text(gross, "#,##0.00"))
+ },
+ {
+ title: "Net pay",
+ value: If(IsBlank(Text(net)), "-", Text(net, "#,##0.00"))
+ },
+ {
+ title: "Status",
+ value: Coalesce(status.descriptor, "-")
+ }
+ ]
+ }
+ ]
+ }
+ )
+ }
+ ],
+ actions: []
+ }
+
+ - kind: CancelAllDialogs
+ id: end_dialogs
+
+ elseActions:
+ - kind: SendActivity
+ id: no_slips
+ activity: No pay slips were found in Workday. If you recently started, pay slips may not be available yet.
+
+ - kind: CancelAllDialogs
+ id: end_no_slips
+
+ elseActions:
+ - kind: SendActivity
+ id: error_msg
+ activity: I was not able to retrieve your pay slips. Please try again or view them directly in Workday.
+
+ - kind: CancelAllDialogs
+ id: end_error
+
+inputType: {}
+outputType: {}
diff --git a/EmployeeSelfServiceAgent/Workday/ExtendedTopics/README.md b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/README.md
new file mode 100644
index 00000000..f0f9b95e
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/README.md
@@ -0,0 +1,95 @@
+# Workday Extended Topics
+
+These topics extend the base ESS agent with additional Workday scenarios. They use the Workday REST API via the WorkdayRESTExecution flow and WorkdaySystemGetRESTExecution system topic included in the base solution. Add only the topics relevant to your organization and also ensure that the OAuth Client in Workday has needed scopes to access the data.
+
+| File | Who uses it | What it does |
+| --- | --- | --- |
+| `GetInboxTasks.yaml` | All employees | Shows open Workday inbox tasks |
+| `GetPaySlips.yaml` | All employees | Shows recent pay slips with pay date, gross, net, and status |
+| `RequestFeedback.yaml` | All employees | Requests feedback from a coworker |
+| `TransferEmployee.yaml` | Managers | Transfers a direct report to a new manager |
+
+## Before you start
+
+The following are included in the **EssITWorkdayHCM** and **EssHRWorkdayHCM** base solutions. Confirm they are active before adding any topic here.
+
+1. In Copilot Studio, go to **Topics** and confirm **WorkdaySystemGetRESTExecution** is present and turned on.
+
+2. In this topic, go to the node where the flow is configured as **WorkdayRESTExecution** and navigate to this flow (Power Automate page) and confirm its status is **On**. If it is off, open it and turn it on. You may be asked to authorize the Workday connection.
+
+If either is missing, import the latest EssITWorkdayHCM or EssHRWorkdayHCM solution first.
+
+## Namespace check
+
+Each topic references other topics by their full namespace. Copilot Studio resolves these automatically based on which solution you have deployed. After saving a topic, verify in the code editor that all `dialog:` references match your solution's namespace.
+
+**EssITWorkdayHCM** — references should use `msdyn_copilotforemployeeselfserviceit.topic.`
+
+**EssHRWorkdayHCM** — references should use `msdyn_copilotforemployeeselfservicehr.topic.`
+
+If any reference does not match, update it manually in the code editor before publishing.
+
+## How to add a topic
+
+Copilot Studio does not support file upload for topics. Add each one using the code editor:
+
+1. Open your ESS agent in Copilot Studio.
+2. Go to **Topics** and click **Add a topic** then **From blank**.
+3. Give the topic a name (see the suggested names in the table below).
+4. Click the **...** menu on the topic and select **Open code editor**.
+5. Select all the existing content, paste in the contents of the `.yaml` file, and save.
+6. Repeat for any other topics you want to add.
+7. Publish the agent when done.
+
+Suggested topic names to use in step 3:
+
+| File | Suggested topic name |
+| --- | --- |
+| `GetInboxTasks.yaml` | Get Employee Inbox Tasks |
+| `GetPaySlips.yaml` | Get Pay Slips |
+| `RequestFeedback.yaml` | Request Feedback on Worker |
+| `TransferEmployee.yaml` | Transfer Employee |
+
+## Configuration
+
+Most topics work without any changes after pasting. The exception is `TransferEmployee.yaml`.
+
+**TransferEmployee.yaml** has one optional setting near the top of the file:
+
+```yaml
+- kind: SetVariable
+ id: set_reason_id
+ variable: Topic.TransferReasonId
+ value: ""
+```
+
+If your Workday tenant requires a job change reason for transfers, replace `""` with your tenant's transfer reason ID (for example `"JOB_CHANGE_REASON-6-5"`). Find this in Workday under **Maintain Job Change Reasons**. If your tenant does not require it, leave the value as `""`.
+
+**RequestFeedback.yaml** requires at least one feedback template to be configured in your Workday tenant. The template list is fetched dynamically at runtime. No changes to the topic file are needed once templates are set up in Workday.
+
+## Adjustable limits
+
+Both `GetInboxTasks.yaml` and `GetPaySlips.yaml` have a variable at the top that controls how many records are fetched. Change the value in the code editor after pasting if needed.
+
+| Topic | Variable | Default | Maximum |
+| --- | --- | --- | --- |
+| GetInboxTasks | `Topic.MaxTasks` | 20 | 100 |
+| GetPaySlips | `Topic.MaxSlips` | 10 | 100 |
+
+## Previews
+
+**Get Inbox Tasks**
+
+
+
+**Request Feedback**
+
+
+
+**Transfer Employee**
+
+
+
+**Get Pay Slips**
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/ExtendedTopics/RequestFeedback.yaml b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/RequestFeedback.yaml
new file mode 100644
index 00000000..788a0f25
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/RequestFeedback.yaml
@@ -0,0 +1,393 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ Allows an employee to request feedback FROM a coworker about themselves.
+ The employee is the subject of the feedback; the coworker (responder) is who provides it.
+
+ Trigger: "I want to request feedback from Carl"
+
+ Steps:
+ 1. Find the coworker (responder) by name search - skipped if name was in the trigger
+ 2. Select a feedback template, sharing preference, and expiration date in one combined card
+ 3. Submit and show confirmation
+
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: ResponderName
+ description: Name of the coworker the employee wants feedback from, if mentioned in the trigger message
+ entity: PersonNamePrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - I want to request feedback from someone
+ - Request feedback from a coworker
+ - Ask Carl for feedback
+ - Get feedback from a colleague
+ - Request peer feedback
+ - I want feedback on my performance
+ - Request feedback
+
+ actions:
+ # Step 1 - Get responder name. Skip prompt if name came from the trigger message.
+ - kind: ConditionGroup
+ id: check_name_in_trigger
+ conditions:
+ - id: name_provided
+ condition: =!IsBlank(Topic.ResponderName)
+ actions:
+ - kind: SetVariable
+ id: use_trigger_name
+ variable: Topic.responderSearch
+ value: =Topic.ResponderName
+
+ elseActions:
+ - kind: Question
+ id: ask_responder_name
+ interruptionPolicy:
+ allowInterruption: false
+ variable: Topic.responderSearch
+ prompt: Who would you like to request feedback from? Please enter their name.
+ entity: StringPrebuiltEntity
+
+ # Search for the coworker by name
+ - kind: BeginDialog
+ id: search_responder
+ displayName: Search for responder by name
+ input:
+ binding:
+ parameters: ="{""search"":""" & Topic.responderSearch & """,""limit"":10,""offset"":0}"
+ operationName: SearchWorkers
+
+ dialog: msdyn_copilotforemployeeselfservice.topic.WorkdaySystemGetRESTExecution
+ output:
+ binding:
+ errorResponse: Topic.searchError
+ isSuccess: Topic.searchSuccess
+ workdayResponse: Topic.searchResponse
+
+ - kind: ConditionGroup
+ id: check_search_result
+ conditions:
+ - id: search_failed
+ condition: =Topic.searchSuccess = false
+ actions:
+ - kind: SendActivity
+ id: search_fail_msg
+ activity: I could not search for that name in Workday. Please try again.
+
+ - kind: CancelAllDialogs
+ id: cancel_search_failed
+
+ - kind: ParseValue
+ id: parse_responder
+ displayName: Parse responder search result
+ variable: Topic.parsedResponder
+ valueType:
+ kind: Record
+ properties:
+ data:
+ type:
+ kind: Table
+ properties:
+ id: String
+ descriptor: String
+ primaryWorkEmail: String
+ businessTitle: String
+ total:
+ type: Number
+
+ value: =Topic.searchResponse
+
+ - kind: ConditionGroup
+ id: check_responder_found
+ conditions:
+ - id: not_found
+ condition: =CountRows(Topic.parsedResponder.data) = 0
+ actions:
+ - kind: SendActivity
+ id: not_found_msg
+ activity: No Workday worker found matching **{Topic.responderSearch}**. Please check the name and try again.
+
+ - kind: CancelAllDialogs
+ id: cancel_not_found
+
+ # Pre-set worker ID and name for single result - no picker needed in the combined form
+ - kind: SetVariable
+ id: pre_set_responder_id
+ variable: Topic.responderWorkerID
+ value: =If(CountRows(Topic.parsedResponder.data) = 1, First(Topic.parsedResponder.data).id, "")
+
+ - kind: SetVariable
+ id: pre_set_responder_name
+ variable: Topic.responderName
+ value: =If(CountRows(Topic.parsedResponder.data) = 1, First(Topic.parsedResponder.data).descriptor, "")
+
+ # Get feedback templates - always scoped to the requesting employee, not the responder
+ - kind: BeginDialog
+ id: get_templates
+ displayName: Get feedback templates
+ input:
+ binding:
+ parameters: ="{""workerID"":""Employee_ID=" & Global.ESS_UserContext_Employee_Id & """,""limit"":20,""offset"":0}"
+ operationName: GetFeedbackTemplates
+
+ dialog: msdyn_copilotforemployeeselfservice.topic.WorkdaySystemGetRESTExecution
+ output:
+ binding:
+ errorResponse: Topic.templateError
+ isSuccess: Topic.templateSuccess
+ workdayResponse: Topic.templateResponse
+
+ - kind: ParseValue
+ id: parse_templates
+ displayName: Parse feedback templates
+ variable: Topic.parsedTemplates
+ valueType:
+ kind: Record
+ properties:
+ data:
+ type:
+ kind: Table
+ properties:
+ id: String
+ descriptor: String
+ total:
+ type: Number
+
+ value: =If(Topic.templateSuccess, Topic.templateResponse, "{""data"":[],""total"":0}")
+
+ # Step 2 - Combined card: worker picker (only when multiple results) + template + sharing + expiration
+ - kind: AdaptiveCardPrompt
+ id: collect_feedback_form
+ displayName: Combined feedback request form
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Request feedback",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true
+ },
+ If(
+ CountRows(Topic.parsedResponder.data) = 1,
+ {
+ type: "TextBlock",
+ text: "Requesting feedback from " & Topic.responderName,
+ wrap: true,
+ spacing: "Small",
+ isSubtle: true
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "selectedResponderId",
+ label: "Select coworker",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a coworker.",
+ choices: ForAll(
+ Topic.parsedResponder.data,
+ {
+ title: descriptor & If(IsBlank(businessTitle), "", " - " & businessTitle) & If(IsBlank(primaryWorkEmail), "", " (" & primaryWorkEmail & ")"),
+ value: id
+ }
+ )
+ }
+ ),
+ {
+ type: "Input.ChoiceSet",
+ id: "feedbackTemplateId",
+ label: "Feedback template",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a feedback template.",
+ spacing: "Medium",
+ choices: If(
+ CountRows(Topic.parsedTemplates.data) = 0,
+ [ { title: "No templates available. Check Workday configuration.", value: "" } ],
+ ForAll(Topic.parsedTemplates.data, { title: descriptor, value: id })
+ )
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "feedbackSharing",
+ label: "Who can see this feedback?",
+ style: "compact",
+ isRequired: false,
+ value: "shareWithMe",
+ choices: [
+ { title: "Share with me only", value: "shareWithMe" },
+ { title: "Also share with my manager", value: "shareWithManager" }
+ ]
+ },
+ {
+ type: "Input.Date",
+ id: "expirationDate",
+ label: "Request expires on",
+ value: Text(DateAdd(Today(), 14, TimeUnit.Days), "yyyy-MM-dd"),
+ isRequired: false
+ }
+ ],
+ actions: [
+ { type: "Action.Submit", title: "Submit Request", id: "Submit" },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", associatedInputs: "none" }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.actionSubmitId
+ selectedResponderId: Topic.selectedResponderId
+ feedbackTemplateId: Topic.feedbackTemplateId
+ feedbackSharing: Topic.feedbackSharing
+ expirationDate: Topic.expirationDate
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ selectedResponderId: String
+ feedbackTemplateId: String
+ feedbackSharing: String
+ expirationDate: String
+
+ - kind: ConditionGroup
+ id: handle_form_cancel
+ conditions:
+ - id: cancelled
+ condition: =Topic.actionSubmitId = "Cancel"
+ actions:
+ - kind: SendActivity
+ id: form_cancel_msg
+ activity: Feedback request cancelled. Let me know if you need anything else.
+
+ - kind: CancelAllDialogs
+ id: cancel_form
+
+ # Resolve final worker ID - use picker selection if multiple results were shown
+ - kind: SetVariable
+ id: resolve_responder_id
+ variable: Topic.responderWorkerID
+ value: =If(IsBlank(Topic.selectedResponderId), Topic.responderWorkerID, Topic.selectedResponderId)
+
+ - kind: SetVariable
+ id: resolve_responder_name
+ variable: Topic.responderName
+ value: =If(IsBlank(Topic.selectedResponderId), Topic.responderName, LookUp(Topic.parsedResponder.data, id = Topic.selectedResponderId).descriptor)
+
+ # Map feedbackSharing choice to Workday fields:
+ # "shareWithMe" - feedbackConfidential=false, showFeedbackProviderName=true
+ # "shareWithManager" - feedbackConfidential=true, showFeedbackProviderName=true
+ - kind: SetVariable
+ id: set_confidential
+ variable: Topic.feedbackConfidential
+ value: =If(Topic.feedbackSharing = "shareWithManager", "true", "false")
+
+ # Step 3 - Submit the feedback request
+ - kind: BeginDialog
+ id: submit_feedback
+ displayName: Submit feedback request
+ input:
+ binding:
+ parameters: ="{""workerID"":""Employee_ID=" & Global.ESS_UserContext_Employee_Id & """,""feedbackTemplateId"":""" & Topic.feedbackTemplateId & """,""feedbackResponders"":[{""id"":""" & Topic.responderWorkerID & """}],""expirationDate"":""" & Topic.expirationDate & """,""feedbackConfidential"":" & Topic.feedbackConfidential & ",""showFeedbackProviderName"":true}"
+ operationName: RequestFeedback
+
+ dialog: msdyn_copilotforemployeeselfservice.topic.WorkdaySystemGetRESTExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ # Step 4 - Show result
+ - kind: ConditionGroup
+ id: show_result
+ conditions:
+ - id: success
+ condition: =Topic.isSuccess = true
+ actions:
+ - kind: ParseValue
+ id: parse_feedback_result
+ variable: Topic.parsedFeedback
+ valueType:
+ kind: Record
+ properties:
+ id: String
+ descriptor: String
+ requestDate: String
+ feedbackOverallStatus: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SendActivity
+ id: success_card
+ activity:
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Feedback request sent",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true
+ },
+ {
+ type: "TextBlock",
+ text: "Your feedback request has been submitted. " & Topic.responderName & " will receive a notification.",
+ wrap: true,
+ spacing: "Small"
+ },
+ {
+ type: "FactSet",
+ spacing: "Medium",
+ facts: [
+ { title: "Feedback from", value: Topic.responderName },
+ { title: "Request date", value: Coalesce(Topic.parsedFeedback.requestDate, Text(Today(), "yyyy-MM-dd")) },
+ { title: "Expires", value: Topic.expirationDate },
+ { title: "Visibility", value: If(Topic.feedbackSharing = "shareWithManager", "Shared with you and your manager", "Visible to you only") }
+ ]
+ }
+ ],
+ actions: []
+ }
+
+ - kind: CancelAllDialogs
+ id: end_success
+
+ elseActions:
+ - kind: AnswerQuestionWithAI
+ id: generate_error
+ autoSend: false
+ variable: Topic.friendlyError
+ userInput: =Topic.errorResponse
+ additionalInstructions: Reframe this Workday error for an employee in one short sentence. Do not apologize or include technical details.
+
+ - kind: SendActivity
+ id: error_msg
+ activity: "{If(IsBlank(Topic.friendlyError), \"The feedback request could not be submitted. Please check that the template is configured correctly in Workday.\", Topic.friendlyError)}"
+
+ - kind: CancelAllDialogs
+ id: end_error
+
+inputType:
+ properties:
+ ResponderName:
+ displayName: ResponderName
+ description: Name of the coworker to request feedback from, if mentioned in the trigger message
+ type: String
+
+outputType: {}
diff --git a/EmployeeSelfServiceAgent/Workday/ExtendedTopics/TransferEmployee.yaml b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/TransferEmployee.yaml
new file mode 100644
index 00000000..99bc642a
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/TransferEmployee.yaml
@@ -0,0 +1,607 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ Allows a manager to transfer one of their direct reports to a different manager.
+ The scenario is "Transfer to Different Manager" (move the worker to a new supervisory org).
+
+ Steps:
+ 1. Manager selects one of their direct reports to transfer
+ 2. Manager provides the new manager's email address
+ 3. Agent resolves the new manager's supervisory org (via REST search)
+ 4. Manager confirms effective date and submits
+ 5. Agent shows confirmation with job change details and Workday link
+
+ NOTE: The jobChangeReason field may be required by your Workday tenant.
+ Set TRANSFER_REASON_ID to your tenant's transfer job change reason ID.
+ If not required, leave it blank and the connector will omit it.
+
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: ReporteeName
+ description: Name of the direct report to transfer, if mentioned in the trigger message
+ entity: PersonNamePrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - Transfer an employee to a new manager
+ - I want to transfer someone to a different manager
+ - Move a direct report to another team
+ - Transfer employee
+ - Reassign a reportee to a new manager
+ - Change manager for an employee
+
+ actions:
+ - kind: BeginDialog
+ id: check_manager
+ displayName: Check manager criteria
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdayManagerCheck
+
+ # ================================================================
+ # CONFIGURATION - customize for your environment
+ # TRANSFER_REASON_ID: your Workday job change reason ID for transfers.
+ # Leave blank ("") if your Workday tenant does not require it.
+ # ================================================================
+ - kind: SetVariable
+ id: set_reason_id
+ displayName: Set job change reason ID
+ variable: Topic.TransferReasonId
+ value: ""
+
+ # Step 1 - Get direct reports and let manager select one
+ - kind: BeginDialog
+ id: get_direct_reports
+ displayName: Get direct reports
+ input:
+ binding:
+ parameters: ="{""workerID"":""Employee_ID=" & Global.ESS_UserContext_Employee_Id & """,""limit"":50,""offset"":0}"
+ operationName: GetWorkerDirectReports
+
+ dialog: msdyn_copilotforemployeeselfservice.topic.WorkdaySystemGetRESTExecution
+ output:
+ binding:
+ errorResponse: Topic.reportsError
+ isSuccess: Topic.reportsSuccess
+ workdayResponse: Topic.reportsResponse
+
+ - kind: ConditionGroup
+ id: check_reports_fetched
+ conditions:
+ - id: reports_failed
+ condition: =Topic.reportsSuccess = false
+ actions:
+ - kind: SendActivity
+ id: reports_error_msg
+ activity: I was not able to retrieve your direct reports right now. Please try again.
+
+ - kind: CancelAllDialogs
+ id: cancel_reports_failed
+
+ - kind: ParseValue
+ id: parse_reports
+ displayName: Parse direct reports
+ variable: Topic.parsedReports
+ valueType:
+ kind: Record
+ properties:
+ data:
+ type:
+ kind: Table
+ properties:
+ id: String
+ descriptor: String
+
+ value: =Topic.reportsResponse
+
+ - kind: ConditionGroup
+ id: check_has_reports
+ conditions:
+ - id: no_reports
+ condition: =CountRows(Topic.parsedReports.data) = 0
+ actions:
+ - kind: SendActivity
+ id: no_reports_msg
+ activity: You do not have any direct reports to transfer.
+
+ - kind: CancelAllDialogs
+ id: cancel_no_reports
+
+ # P1: Autofill reportee from trigger message if name was provided
+ - kind: SetVariable
+ id: autofill_reportee
+ displayName: Autofill reportee selection if name was provided
+ variable: Topic.autofillReporteeId
+ value: =If(IsBlank(Topic.ReporteeName), "", Coalesce(LookUp(Topic.parsedReports.data, descriptor = Topic.ReporteeName).id, LookUp(Topic.parsedReports.data, StartsWith(descriptor, Topic.ReporteeName)).id, ""))
+
+ - kind: AdaptiveCardPrompt
+ id: select_reportee
+ displayName: Select direct report to transfer
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Transfer an employee to a new manager",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true
+ },
+ {
+ type: "TextBlock",
+ text: "Select the direct report you want to transfer:",
+ wrap: true,
+ spacing: "Small"
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "reporteeId",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select an employee to transfer.",
+ value: Topic.autofillReporteeId,
+ choices: ForAll(
+ Topic.parsedReports.data,
+ { title: descriptor, value: id }
+ )
+ }
+ ],
+ actions: [
+ { type: "Action.Submit", title: "Next", id: "Next" },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", associatedInputs: "none" }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.actionSubmitId
+ reporteeId: Topic.targetWorkerID
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ reporteeId: String
+
+ - kind: ConditionGroup
+ id: handle_reportee_cancel
+ conditions:
+ - id: cancelled
+ condition: =Topic.actionSubmitId = "Cancel"
+ actions:
+ - kind: SendActivity
+ id: cancel_msg
+ activity: Transfer cancelled. Let me know if you need anything else.
+
+ - kind: CancelAllDialogs
+ id: cancel_all
+
+ - kind: SetVariable
+ id: set_reportee_name
+ variable: Topic.targetWorkerName
+ value: =LookUp(Topic.parsedReports.data, id = Topic.targetWorkerID).descriptor
+
+ # Step 2 - Ask for new manager's name
+ - kind: AdaptiveCardPrompt
+ id: collect_transfer_details
+ displayName: Collect new manager details and effective date
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Transfer " & Topic.targetWorkerName,
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true
+ },
+ {
+ type: "TextBlock",
+ text: "Provide the new manager's details:",
+ wrap: true,
+ spacing: "Small"
+ },
+ {
+ type: "Input.Text",
+ id: "newManagerSearch",
+ label: "New manager's name",
+ placeholder: "Search by name...",
+ isRequired: true,
+ errorMessage: "Please enter the new manager's name."
+ },
+ {
+ type: "Input.Date",
+ id: "effectiveDate",
+ label: "Effective date for the transfer",
+ value: Text(Today(), "yyyy-MM-dd"),
+ isRequired: true,
+ errorMessage: "Please provide an effective date."
+ }
+ ],
+ actions: [
+ { type: "Action.Submit", title: "Next", id: "Next" },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", associatedInputs: "none" }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.actionSubmitId2
+ newManagerSearch: Topic.newManagerSearch
+ effectiveDate: Topic.effectiveDate
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ newManagerSearch: String
+ effectiveDate: String
+
+ - kind: ConditionGroup
+ id: handle_details_cancel
+ conditions:
+ - id: cancelled
+ condition: =Topic.actionSubmitId2 = "Cancel"
+ actions:
+ - kind: SendActivity
+ id: cancel_msg2
+ activity: Transfer cancelled. Let me know if you need anything else.
+
+ - kind: CancelAllDialogs
+ id: cancel_all2
+
+ # Step 3 - Search for new manager by name to get their worker ID
+ - kind: BeginDialog
+ id: search_new_manager
+ displayName: Search for new manager by name
+ input:
+ binding:
+ parameters: ="{""search"":""" & Topic.newManagerSearch & """,""limit"":5,""offset"":0}"
+ operationName: SearchWorkers
+
+ dialog: msdyn_copilotforemployeeselfservice.topic.WorkdaySystemGetRESTExecution
+ output:
+ binding:
+ errorResponse: Topic.searchError
+ isSuccess: Topic.searchSuccess
+ workdayResponse: Topic.searchResponse
+
+ - kind: ConditionGroup
+ id: check_manager_found
+ conditions:
+ - id: search_failed
+ condition: =Topic.searchSuccess = false
+ actions:
+ - kind: SendActivity
+ id: search_fail_msg
+ activity: I could not search for that name in Workday. Please try again.
+
+ - kind: CancelAllDialogs
+ id: cancel_search_failed
+
+ - kind: ParseValue
+ id: parse_manager_search
+ displayName: Parse new manager search result
+ variable: Topic.parsedManager
+ valueType:
+ kind: Record
+ properties:
+ data:
+ type:
+ kind: Table
+ properties:
+ id: String
+ descriptor: String
+ primaryWorkEmail: String
+ businessTitle: String
+ total:
+ type: Number
+
+ value: =Topic.searchResponse
+
+ - kind: ConditionGroup
+ id: check_manager_result
+ conditions:
+ - id: not_found
+ condition: =CountRows(Topic.parsedManager.data) = 0
+ actions:
+ - kind: SendActivity
+ id: not_found_msg
+ activity: No Workday worker found matching **{Topic.newManagerSearch}**. Please check the name and try again.
+
+ - kind: CancelAllDialogs
+ id: cancel_not_found
+
+ - id: single_result
+ condition: =CountRows(Topic.parsedManager.data) = 1
+ actions:
+ - kind: SetVariable
+ id: set_new_manager_id_single
+ variable: Topic.newManagerWorkerID
+ value: =First(Topic.parsedManager.data).id
+
+ - kind: SetVariable
+ id: set_new_manager_name_single
+ variable: Topic.newManagerName
+ value: =First(Topic.parsedManager.data).descriptor
+
+ elseActions:
+ - kind: AdaptiveCardPrompt
+ id: pick_manager
+ displayName: Ask manager to select from multiple results
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Select the new manager",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true
+ },
+ {
+ type: "TextBlock",
+ text: "Multiple workers match. Please select the correct manager:",
+ wrap: true,
+ spacing: "Small",
+ isSubtle: true
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "selectedManagerId",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a manager.",
+ choices: ForAll(
+ Topic.parsedManager.data,
+ {
+ title: descriptor & If(IsBlank(businessTitle), "", " - " & businessTitle) & If(IsBlank(primaryWorkEmail), "", " (" & primaryWorkEmail & ")"),
+ value: id
+ }
+ )
+ }
+ ],
+ actions: [
+ { type: "Action.Submit", title: "Select", id: "Select" },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", associatedInputs: "none" }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.actionSubmitId3
+ selectedManagerId: Topic.selectedManagerId
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ selectedManagerId: String
+
+ - kind: ConditionGroup
+ id: handle_manager_pick_cancel
+ conditions:
+ - id: cancelled
+ condition: =Topic.actionSubmitId3 = "Cancel"
+ actions:
+ - kind: SendActivity
+ id: manager_pick_cancel_msg
+ activity: Transfer cancelled. Let me know if you need anything else.
+
+ - kind: CancelAllDialogs
+ id: cancel_manager_pick
+
+ - kind: SetVariable
+ id: set_new_manager_id_multi
+ variable: Topic.newManagerWorkerID
+ value: =Topic.selectedManagerId
+
+ - kind: SetVariable
+ id: set_new_manager_name_multi
+ variable: Topic.newManagerName
+ value: =LookUp(Topic.parsedManager.data, id = Topic.selectedManagerId).descriptor
+
+ # Step 4 - Get supervisory org managed by the new manager
+ - kind: BeginDialog
+ id: get_manager_org
+ displayName: Get supervisory org managed by new manager
+ input:
+ binding:
+ parameters: ="{""workerID"":""" & Topic.newManagerWorkerID & """,""limit"":5,""offset"":0}"
+ operationName: GetSupervisoryOrganizationsManaged
+
+ dialog: msdyn_copilotforemployeeselfservice.topic.WorkdaySystemGetRESTExecution
+ output:
+ binding:
+ errorResponse: Topic.orgError
+ isSuccess: Topic.orgSuccess
+ workdayResponse: Topic.orgResponse
+
+ - kind: ConditionGroup
+ id: check_org_found
+ conditions:
+ - id: org_failed
+ condition: =Topic.orgSuccess = false
+ actions:
+ - kind: SendActivity
+ id: org_fail_msg
+ activity: I found **{Topic.newManagerName}** in Workday but could not retrieve their supervisory organization. They may not manage a team yet.
+
+ - kind: CancelAllDialogs
+ id: cancel_org_failed
+
+ - kind: ParseValue
+ id: parse_org
+ displayName: Parse supervisory org
+ variable: Topic.parsedOrg
+ valueType:
+ kind: Record
+ properties:
+ data:
+ type:
+ kind: Table
+ properties:
+ id: String
+ descriptor: String
+ total:
+ type: Number
+
+ value: =Topic.orgResponse
+
+ - kind: ConditionGroup
+ id: check_org_exists
+ conditions:
+ - id: no_org
+ condition: =CountRows(Topic.parsedOrg.data) = 0
+ actions:
+ - kind: SendActivity
+ id: no_org_msg
+ activity: "**{Topic.newManagerName}** does not manage a supervisory organization in Workday. A manager must have an active org before an employee can be transferred to them."
+
+ - kind: CancelAllDialogs
+ id: cancel_no_org
+
+ # Use the first org (the primary one). If manager has multiple orgs, use the first.
+ - kind: SetVariable
+ id: set_org_id
+ variable: Topic.supervisoryOrgId
+ value: =First(Topic.parsedOrg.data).id
+
+ - kind: SetVariable
+ id: set_org_name
+ variable: Topic.supervisoryOrgName
+ value: =First(Topic.parsedOrg.data).descriptor
+
+ # Step 5 - Confirm and submit
+ - kind: Question
+ id: confirm_transfer
+ interruptionPolicy:
+ allowInterruption: false
+ variable: Topic.confirmTransfer
+ prompt: |-
+ Please confirm the transfer details:
+ - **Employee:** {Topic.targetWorkerName}
+ - **New manager:** {Topic.newManagerName}
+ - **New supervisory org:** {Topic.supervisoryOrgName}
+ - **Effective date:** {Topic.effectiveDate}
+
+ Proceed with the transfer?
+ entity: BooleanPrebuiltEntity
+
+ - kind: ConditionGroup
+ id: handle_confirm
+ conditions:
+ - id: not_confirmed
+ condition: =Topic.confirmTransfer = false
+ actions:
+ - kind: SendActivity
+ id: not_confirmed_msg
+ activity: Transfer cancelled. Let me know if you need anything else.
+
+ - kind: CancelAllDialogs
+ id: cancel_not_confirmed
+
+ - kind: BeginDialog
+ id: submit_transfer
+ displayName: Submit employee transfer
+ input:
+ binding:
+ parameters: ="{""workerID"":""" & Topic.targetWorkerID & """,""supervisoryOrganizationId"":""" & Topic.supervisoryOrgId & """" & If(IsBlank(Topic.TransferReasonId), "", ",""jobChangeReasonId"":""" & Topic.TransferReasonId & """") & ",""effective"":""" & Topic.effectiveDate & """,""moveManagersTeam"":false}"
+ operationName: TransferEmployee
+
+ dialog: msdyn_copilotforemployeeselfservice.topic.WorkdaySystemGetRESTExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ConditionGroup
+ id: show_result
+ conditions:
+ - id: success
+ condition: =Topic.isSuccess = true
+ actions:
+ - kind: ParseValue
+ id: parse_transfer_result
+ variable: Topic.parsedTransfer
+ valueType:
+ kind: Record
+ properties:
+ id: String
+ descriptor: String
+ effective: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SendActivity
+ id: success_card
+ activity:
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Transfer submitted",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true
+ },
+ {
+ type: "TextBlock",
+ text: "The job change has been submitted in Workday and is pending processing.",
+ wrap: true,
+ spacing: "Small"
+ },
+ {
+ type: "FactSet",
+ spacing: "Medium",
+ facts: [
+ { title: "Employee", value: Topic.targetWorkerName },
+ { title: "New manager", value: Topic.newManagerName },
+ { title: "Effective date", value: Coalesce(Topic.parsedTransfer.effective, Topic.effectiveDate) },
+ { title: "Job change ID", value: Topic.parsedTransfer.id }
+ ]
+ }
+ ],
+ actions: []
+ }
+
+ - kind: CancelAllDialogs
+ id: end_success
+
+ elseActions:
+ - kind: AnswerQuestionWithAI
+ id: generate_error
+ autoSend: false
+ variable: Topic.friendlyError
+ userInput: =Topic.errorResponse
+ additionalInstructions: Reframe this Workday error for a manager in one short sentence. Do not apologize or include technical IDs.
+
+ - kind: SendActivity
+ id: error_msg
+ activity: "{If(IsBlank(Topic.friendlyError), \"The transfer could not be submitted. Please verify the details in Workday.\", Topic.friendlyError)}"
+
+ - kind: CancelAllDialogs
+ id: end_error
+
+inputType:
+ properties:
+ ReporteeName:
+ displayName: ReporteeName
+ description: Name of the direct report to transfer, if mentioned in the trigger message
+ type: String
+
+outputType: {}
diff --git a/EmployeeSelfServiceAgent/Workday/ExtendedTopics/assets/GetInboxTasks.png b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/assets/GetInboxTasks.png
new file mode 100644
index 00000000..433a500a
Binary files /dev/null and b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/assets/GetInboxTasks.png differ
diff --git a/EmployeeSelfServiceAgent/Workday/ExtendedTopics/assets/GetPaySlips.png b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/assets/GetPaySlips.png
new file mode 100644
index 00000000..77323790
Binary files /dev/null and b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/assets/GetPaySlips.png differ
diff --git a/EmployeeSelfServiceAgent/Workday/ExtendedTopics/assets/RequestFeedback.png b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/assets/RequestFeedback.png
new file mode 100644
index 00000000..7bed0699
Binary files /dev/null and b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/assets/RequestFeedback.png differ
diff --git a/EmployeeSelfServiceAgent/Workday/ExtendedTopics/assets/TransferEmployee.png b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/assets/TransferEmployee.png
new file mode 100644
index 00000000..3e60dd18
Binary files /dev/null and b/EmployeeSelfServiceAgent/Workday/ExtendedTopics/assets/TransferEmployee.png differ
diff --git a/EmployeeSelfServiceAgent/Workday/ManagerScenarios/README.md b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/README.md
new file mode 100644
index 00000000..89ed91da
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/README.md
@@ -0,0 +1,11 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Manager Scenarios
+
+Copilot Studio topics for manager self-service actions in Workday.
+
+| Folder | Description |
+| --- | --- |
+| [WorkdayGetManagerReporteesTimeInPosition/](./WorkdayGetManagerReporteesTimeInPosition/) | View reportees' time in position |
diff --git a/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/README.md b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/README.md
new file mode 100644
index 00000000..da976131
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/README.md
@@ -0,0 +1,110 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Get Manager Reportees Time In Position
+
+## Overview
+This scenario enables managers to view their direct reports along with how long each employee has been in their current position. It retrieves team member information from Workday HCM and calculates the time in position for each reportee.
+
+## Files
+
+| File | Description |
+|------|-------------|
+| `topic.yaml` | Copilot Studio topic definition with trigger phrases and Power Fx logic |
+| `msdyn_GetManagerReportees.xml` | XML template for Workday Get_Workers API call |
+
+## Prerequisites
+
+### Global Variables Required
+- `Global.ESS_UserContext_ManagerOrganizationId` - The manager's organization ID in Workday (used to filter direct reports)
+
+### Workday API
+- **Service**: Human_Resources
+- **Operation**: Get_Workers
+- **Version**: v42.0
+
+## Scenario Details
+
+### What It Does
+1. Retrieves all employees in the manager's organization (direct reports)
+2. Extracts key employee information (Name, Title, Position Start Date, etc.)
+3. Calculates Time in Position for each employee using Power Fx DateDiff functions
+4. Presents results with tenure information formatted as "X years, Y months, Z days"
+
+### Data Retrieved
+| Field | Description |
+|-------|-------------|
+| EmployeeID | Workday Employee ID |
+| Name | Employee's legal formatted name |
+| BusinessTitle | Current job title |
+| WorkerType | Employee or Contingent Worker |
+| JobProfile | Job profile name |
+| Location | Business site/location name |
+| PositionStartDate | Date employee started current position |
+| HireDate | Original hire date |
+| Status | Active/Inactive status |
+| TimeInPositionYears | Calculated years in current position |
+| TimeInPositionMonths | Calculated remaining months |
+| TimeInPositionDays | Calculated remaining days |
+| TimeInPosition | Formatted string (e.g., "2 years, 3 months, 15 days") |
+
+### Trigger Phrases
+- "Show me my team's time in position"
+- "How long have my direct reports been in their roles"
+- "Get reportees time in current position"
+- "List my team members with their position tenure"
+- "Who on my team has been in their role the longest"
+- "Show tenure for my direct reports"
+- "My reportees job tenure"
+- "Time in position for my team"
+
+## Time In Position Calculation
+
+The scenario uses Power Fx formulas to calculate time in position:
+
+```
+TimeInPositionYears = Int(DateDiff(DateValue(PositionStartDate), Now(), TimeUnit.Months) / 12)
+TimeInPositionMonths = Mod(DateDiff(DateValue(PositionStartDate), Now(), TimeUnit.Months), 12)
+TimeInPositionDays = DateDiff(DateAdd(DateAdd(DateValue(PositionStartDate), Years, TimeUnit.Years), Months, TimeUnit.Months), Now(), TimeUnit.Days)
+```
+
+## Response Group Configuration
+
+The XML template is optimized for performance by:
+- **Including**: Reference, Personal Information, Employment Information
+- **Excluding**: Compensation, Benefits, Documents, Photos, and other unnecessary data
+- **Filtering**: Only active workers (`Exclude_Inactive_Workers: true`)
+
+## Setup Instructions
+
+1. **Import the Topic**: Import `topic.yaml` into your Copilot Studio agent
+2. **Add XML Template**: Upload `msdyn_GetManagerReportees.xml` to your Workday connector configuration
+3. **Configure Connection**: Ensure your Workday connector connection reference is properly set in the topic
+4. **Set Global Variable**: Make sure `Global.ESS_UserContext_ManagerOrganizationId` is populated (typically from user authentication context)
+
+## Model Instructions
+
+The topic includes model instructions for the AI to:
+- Display results as a nested markdown list
+- Format Time in Position clearly
+- Show Name, Job Title, Time in Position, Start Date, and Status for each reportee
+- Sort or group results based on user's question context
+
+## Example Output
+
+When a manager asks "Show me my team's time in position", the agent displays:
+
+```
+Here are your direct reports and their time in current position:
+
+- **John Smith** - Senior Developer
+ - Time in Position: 2 years, 5 months, 12 days
+ - Position Start: 2022-07-15
+ - Status: Active
+
+- **Jane Doe** - Product Manager
+ - Time in Position: 1 year, 2 months, 3 days
+ - Position Start: 2023-10-20
+ - Status: Active
+```
diff --git a/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/msdyn_GetManagerReportees.xml b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/msdyn_GetManagerReportees.xml
new file mode 100644
index 00000000..72a67a03
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/msdyn_GetManagerReportees.xml
@@ -0,0 +1,124 @@
+
+
+
+
+ User
+
+ Template_GetManagerReporteesRequest
+ Human_Resources
+ v42.0
+
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Worker_ID']/text()
+ EmployeeID
+
+
+ //*[local-name()='Worker_Descriptor']/text()
+ Name
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Job_Data']/*[local-name()='Position_Data']/*[local-name()='Business_Title']/text()
+ BusinessTitle
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Job_Data']/*[local-name()='Position_Data']/*[local-name()='Worker_Type_Reference']/@*[local-name()='Descriptor']
+ WorkerType
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Job_Data']/*[local-name()='Position_Data']/*[local-name()='Job_Profile_Summary_Data']/*[local-name()='Job_Profile_Reference']/@*[local-name()='Descriptor']
+ JobProfile
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Job_Data']/*[local-name()='Position_Data']/*[local-name()='Business_Site_Summary_Data']/*[local-name()='Location_Reference']/@*[local-name()='Descriptor']
+ Location
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Job_Data']/*[local-name()='Position_Data']/*[local-name()='Start_Date']/text()
+ PositionStartDate
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Status_Data']/*[local-name()='Hire_Date']/text()
+ HireDate
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Status_Data']/*[local-name()='Active']/text()
+ Status
+
+
+
+
+
+
+
+
+
+
+ {Manager_Org_ID}
+
+
+
+ true
+ false
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/topic.yaml b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/topic.yaml
new file mode 100644
index 00000000..b7ddb5e5
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/topic.yaml
@@ -0,0 +1,182 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ You will respond to requests about time in position for direct reports of the user making the request.
+
+ For info on single direct report's time in position:
+ Heading - "[Report]'s time in position"
+ Verbatim line after heading: "[Report] has been in their current role as a [Position] for [LengthOfService]. I pulled this from Workday, an HR platform your company uses."
+ Sections as bold headers and non-bold bullets under: More about [Direct Report]'s position
+
+ For info on all direct reports time in position: Display in Table
+ Heading - "Team roles and time in position"
+ Verbatim line after heading: "Here's a list of your team members and how long they've been in their positions. I pulled this from Workday, an HR platform your company uses."
+ Table columns: Name, Title, Time in Position
+
+ Common Rules ALWAYS FOLLOW:
+ - ALWAYS add large sized heading with sentence case
+ - Introduction (Relevant) RIGHT AFTER HEADING & ADD SECTION LINE AFTER IT,
+ - Double check if rules are followed
+ Invalid: non-direct reports.
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent: {}
+ actions:
+ - kind: BeginDialog
+ id: jJpz3n
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Manager_Org_ID}"",""value"":""" & Global.ESS_UserContext_ManagerOrganizationId & """}]}"
+ scenarioName: msdyn_GetManagerReportees
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: aXVFNu
+ displayName: Parse value to a record
+ variable: Topic.workdayResponseRecord
+ valueType:
+ kind: Record
+ properties:
+ BusinessTitle:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ EmployeeID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ HireDate:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ JobProfile:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ Location:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ Name:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ PositionStartDate:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ Status:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ WorkerType:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: setVariable_ztXmui
+ displayName: Extract data to table
+ variable: Topic.workdayResponseTable
+ value: |-
+ =ForAll(
+ Sequence(CountRows(Topic.workdayResponseRecord.EmployeeID)),
+ {
+ EmployeeID: Last(FirstN(Topic.workdayResponseRecord.EmployeeID, Value)).Value,
+ Name: Last(FirstN(Topic.workdayResponseRecord.Name, Value)).Value,
+ BusinessTitle: Last(FirstN(Topic.workdayResponseRecord.BusinessTitle, Value)).Value,
+ WorkerType: Last(FirstN(Topic.workdayResponseRecord.WorkerType, Value)).Value,
+ JobProfile: Last(FirstN(Topic.workdayResponseRecord.JobProfile, Value)).Value,
+ Location: Last(FirstN(Topic.workdayResponseRecord.Location, Value)).Value,
+ PositionStartDate: Last(FirstN(Topic.workdayResponseRecord.PositionStartDate, Value)).Value,
+ HireDate: Last(FirstN(Topic.workdayResponseRecord.HireDate, Value)).Value,
+ Status: If(Last(FirstN(Topic.workdayResponseRecord.Status, Value)).Value = "1", "Active", "Inactive")
+ }
+ )
+
+ - kind: SetVariable
+ id: setVariable_AddTimeInPosition
+ displayName: Add Time in Position calculations
+ variable: Topic.workdayResponseTableWithTimeInPosition
+ value: |-
+ =ForAll(
+ Topic.workdayResponseTable,
+ With(
+ {
+ positionDate: DateValue(PositionStartDate),
+ yearsCalc: RoundDown(DateDiff(DateValue(PositionStartDate), Today(), TimeUnit.Months) / 12, 0),
+ monthsCalc: Mod(DateDiff(DateValue(PositionStartDate), Today(), TimeUnit.Months), 12),
+ daysCalc: DateDiff(
+ DateAdd(DateValue(PositionStartDate), DateDiff(DateValue(PositionStartDate), Today(), TimeUnit.Months), TimeUnit.Months),
+ Today(),
+ TimeUnit.Days
+ )
+ },
+ {
+ EmployeeID: EmployeeID,
+ Name: Name,
+ BusinessTitle: BusinessTitle,
+ WorkerType: WorkerType,
+ JobProfile: JobProfile,
+ Location: Location,
+ PositionStartDate: PositionStartDate,
+ HireDate: HireDate,
+ Status: Status,
+ TimeInPositionYears: yearsCalc,
+ TimeInPositionMonths: monthsCalc,
+ TimeInPositionDays: daysCalc,
+ TimeInPosition:
+ If(yearsCalc > 0, yearsCalc & " year" & If(yearsCalc > 1, "s", "") & " ", "") &
+ If(monthsCalc > 0, monthsCalc & " month" & If(monthsCalc > 1, "s", "") & " ", "") &
+ daysCalc & " day" & If(daysCalc <> 1, "s", "")
+ }
+ )
+ )
+
+inputType: {}
+outputType:
+ properties:
+ workdayResponseTableWithTimeInPosition:
+ displayName: workdayResponseTableWithTimeInPosition
+ type:
+ kind: Table
+ properties:
+ BusinessTitle: String
+ EmployeeID: String
+ HireDate: String
+ JobProfile: String
+ Location: String
+ Name: String
+ PositionStartDate: String
+ Status: String
+ TimeInPosition: String
+ TimeInPositionDays: Number
+ TimeInPositionMonths: Number
+ TimeInPositionYears: Number
+ WorkerType: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagerServiceAnniversary/msdyn_HRWorkdayHCMManagerServiceAnniversary.xml b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagerServiceAnniversary/msdyn_HRWorkdayHCMManagerServiceAnniversary.xml
new file mode 100644
index 00000000..54aa264c
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagerServiceAnniversary/msdyn_HRWorkdayHCMManagerServiceAnniversary.xml
@@ -0,0 +1,80 @@
+
+
+
+
+ User
+
+ msdyn_HRWorkdayHCMManagerServiceAnniversary_GetWorkersManagerRequest
+ Human_Resources
+ v41.0
+
+
+
+ Include_Employment_Information
+ true
+
+
+ Include_Personal_Information
+ true
+
+
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Worker_ID']/text()
+ WorkerID
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/*[local-name()='First_Name']/text()
+
+ FirstName
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/*[local-name()='Last_Name']/text()
+ LastName
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Status_Data']/*[local-name()='Hire_Date']/text()
+ HireDate
+
+
+
+
+
+
+
+
+
+
+ {ManagerOrgId}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagerServiceAnniversary/topic.yaml b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagerServiceAnniversary/topic.yaml
new file mode 100644
index 00000000..367c5fa9
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagerServiceAnniversary/topic.yaml
@@ -0,0 +1,225 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ You will respond to requests about the service anniversary of direct reports of the user making the request. Your information is retrieved from Workday, and will only contain results regarding the user's direct reports. There is no information available for anyone who isn't a direct report. The resulting data will contain a list of employees who report to the requestor and will contain their service anniversary data. You must NOT give data if you do not have enough info.
+
+ Ex. invalid requests:
+ "What is my manager's service anniversary?"
+ "What is my sister's hire date?"
+
+ Ex. valid requests:
+ "What is the service anniversary of my direct reports?"
+
+ If question contains "next month" use isAnniversaryNextMonth to filter. Unless specified, the response should always be future anniversaries. Include the following columns in your response Employee Name, Hire Date, Upcoming Service Anniversary Date, Upcoming Milestone
+ Your output **must** be a table in markdown language and **must** be based on {Topic.response}.
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: EmployeeName
+ description: Employee name whose service anniversary needs to be retrieved
+ entity: PersonNamePrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+ - kind: AutomaticTaskInput
+ propertyName: duration
+ description: Duration used to calculate nth service anniversary date
+ entity: NumberPrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - When are the service anniversaries of all my directs?
+ - What are the service anniversaries of my entire team?
+ - Show me the service anniversaries of my reports?
+ - What are the upcoming service anniversaries of my team?
+ - Any upcoming service anniversaries of my reports?
+ - Show me service anniversaries of my directs?
+ - What is [EmployeeName]'s next service anniversary?
+ - When is [EmployeeName]'s next year service anniversary?
+
+ actions:
+ - kind: BeginDialog
+ id: WmAHrc
+ displayName: Check manager criteria
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdayManagerCheck
+
+ - kind: SetVariable
+ id: setVariable_FrkyzI
+ displayName: Set current date
+ variable: Topic.currentDate
+ value: =Now()
+
+ - kind: BeginDialog
+ id: HZDypL
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{ManagerOrgId}"",""value"":""" & Global.ESS_UserContext_ManagerOrganizationId & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMManagerServiceAnniversary
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: Nh88X0
+ displayName: Parse Workday response
+ variable: Topic.parsedWorkdayResponse
+ valueType:
+ kind: Record
+ properties:
+ FirstName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ HireDate:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ LastName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ WorkerID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: setVariable_eJUuES
+ displayName: Build Workday response table
+ variable: Topic.workdayResponseTable
+ value: |-
+ =ForAll(
+ Sequence(CountRows(Topic.parsedWorkdayResponse.WorkerID)),
+ {
+ FirstName: Last(FirstN(Topic.parsedWorkdayResponse.FirstName, Value)).Value,
+ LastName: Last(FirstN(Topic.parsedWorkdayResponse.LastName, Value)).Value,
+ HireDate: Last(FirstN(Topic.parsedWorkdayResponse.HireDate, Value)).Value
+ }
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_P6QlNE
+ conditions:
+ - id: conditionItem_3RSvtM
+ condition: =!IsBlank(Topic.EmployeeName)
+ displayName: Check if EmployeeName provided
+ actions:
+ - kind: SetVariable
+ id: setVariable_yRTs4g
+ displayName: Filter for the given EmployeeName
+ variable: Topic.workdayResponseTable
+ value: |-
+ =Filter(
+ Topic.workdayResponseTable,
+ FirstName = Topic.EmployeeName Or
+ LastName = Topic.EmployeeName Or
+ Concatenate(FirstName, " ", LastName) = Topic.EmployeeName Or
+ Concatenate(LastName, " ", FirstName) = Topic.EmployeeName
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_5aSdys
+ conditions:
+ - id: conditionItem_U6JgqP
+ condition: =IsEmpty(Topic.workdayResponseTable)
+ displayName: Check is filtered data empty
+ actions:
+ - kind: SendActivity
+ id: sendActivity_ToPxHZ
+ displayName: Respond with no access message
+ activity: It looks like you don't have access to this information. Try making a new request.
+
+ - kind: SetVariable
+ id: setVariable_bqlpWp
+ displayName: Clear response table
+ variable: Topic.workdayResponseTable
+ value: =[]
+
+ - kind: CancelAllDialogs
+ id: PsP6gr
+
+ - kind: SetVariable
+ id: setVariable_CxBy4Y
+ displayName: Set response to formatted Workday response
+ variable: Topic.response
+ value: |-
+ =ForAll(Topic.workdayResponseTable, {
+ EmployeeName: 'FirstName' & " " & 'LastName',
+ HireDate: DateValue('HireDate'),
+ UpcomingServiceAnniversaryDate: If(
+ Today() <= DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years), TimeUnit.Years),
+ DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years), TimeUnit.Years),
+ DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years) + 1, TimeUnit.Years)
+ ),
+ UpcomingMilestone: DateDiff(DateValue('HireDate'), If(
+ Today() <= DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years), TimeUnit.Years),
+ DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years), TimeUnit.Years),
+ DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years) + 1, TimeUnit.Years)
+ ), TimeUnit.Years),
+ AnniversaryMonth: Month(If(
+ Today() <= DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years), TimeUnit.Years),
+ DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years), TimeUnit.Years),
+ DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years) + 1, TimeUnit.Years)
+ )),
+ isAnniversaryNextMonth: Month(If(
+ Today() <= DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years), TimeUnit.Years),
+ DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years), TimeUnit.Years),
+ DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years) + 1, TimeUnit.Years)
+ )) = Month(Now()) + 1
+ })
+
+ - kind: SetVariable
+ id: setVariable_AnybBj
+ displayName: Clear workday response
+ variable: Topic.workdayResponse
+ value: "\"\""
+
+ - kind: EndDialog
+ id: Y2VqKn
+
+inputType:
+ properties:
+ duration:
+ displayName: duration
+ description: Duration used to calculate nth service anniversary date
+ type: Number
+
+ EmployeeName:
+ displayName: EmployeeName
+ description: Employee name whose service anniversary needs to be retrieved
+ type: String
+
+outputType:
+ properties:
+ response:
+ displayName: response
+ type:
+ kind: Table
+ properties:
+ AnniversaryMonth: Number
+ EmployeeName: String
+ HireDate: Date
+ isAnniversaryNextMonth: Boolean
+ UpcomingMilestone: Number
+ UpcomingServiceAnniversaryDate: Date
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-CompanyCode/msdyn_HRWorkdayHCMManagerDirectCompanyCode.xml b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-CompanyCode/msdyn_HRWorkdayHCMManagerDirectCompanyCode.xml
new file mode 100644
index 00000000..4773a2e3
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-CompanyCode/msdyn_HRWorkdayHCMManagerDirectCompanyCode.xml
@@ -0,0 +1,84 @@
+
+
+
+
+ User
+
+ Template_GetWorkersManagerRequest
+ Human_Resources
+ v41.0
+
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Worker_ID']/text()
+ WorkerID
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/*[local-name()='First_Name']/text()
+
+ FirstName
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/*[local-name()='Last_Name']/text()
+ LastName
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Organization_Data']/*[local-name()='Worker_Organization_Data'][*[local-name()='Organization_Data']/*[local-name()='Organization_Subtype_Reference']/*[local-name()='ID'][@*[local-name()='type']='Organization_Subtype_ID' and text()='Company']]/*[local-name()='Organization_Data']/*[local-name()='Organization_Code']/text()
+ CompanyCode
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Organization_Data']/*[local-name()='Worker_Organization_Data'][*[local-name()='Organization_Data']/*[local-name()='Organization_Subtype_Reference']/*[local-name()='ID'][@*[local-name()='type']='Organization_Subtype_ID' and text()='Company']]/*[local-name()='Organization_Data']/*[local-name()='Organization_Name']/text()
+ CompanyName
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Job_Data']/*[local-name()='Position_Data']/*[local-name()='Position_ID']/text()
+ PositionID
+
+
+
+
+
+
+
+
+
+
+ {ManagerOrgId}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+ false
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-CompanyCode/topic.yaml b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-CompanyCode/topic.yaml
new file mode 100644
index 00000000..af8d36f2
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-CompanyCode/topic.yaml
@@ -0,0 +1,165 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ You will respond to requests about the Company Code or Company Name of direct reports of the user making the request. Your information is retrieved from Workday, and will only contain results regarding the user's direct reports. There is no information available for anyone who isn't a direct report, and being a direct report means that the individual is not a manager, spouse, sibling, or any other relationship to the requestor, and only means that they report to the requestor.
+
+ Example invalid requests:
+ "What is my manager's company code?"
+ "What is my sister's company code?"
+
+ Example valid requests:
+ "What is the company code of my direct reports?"
+
+ Your output **must** be a nested list in markdown language based on the data contained in the {Topic.workdayResponseTable} variable.
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: EmployeeName
+ description: Employee name whose company code needs to be fetched
+ entity: PersonNamePrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - Show me my team’s Company codes?
+ - What are the company codes for my reports?
+ - What company codes are mapped to my team members?
+
+ actions:
+ - kind: BeginDialog
+ id: dAhS4T
+ displayName: Check manager status
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdayManagerCheck
+
+ - kind: BeginDialog
+ id: Gt044B
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{ManagerOrgId}"",""value"":""" & Global.ESS_UserContext_ManagerOrganizationId & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMManagerDirectCompanyCode
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: aIxWzK
+ displayName: Parse value to a table
+ variable: Topic.WorkdayResponseRecord
+ valueType:
+ kind: Record
+ properties:
+ CompanyCode:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ CompanyName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ FirstName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ LastName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ WorkerID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: HC2h7w
+ displayName: Merge all records in table
+ variable: Topic.workdayResponseTable
+ value: |-
+ =ForAll(
+ Sequence(CountRows(Topic.WorkdayResponseRecord.WorkerID)),
+ {
+ FirstName: Last(FirstN(Topic.WorkdayResponseRecord.FirstName, Value)).Value,
+ LastName: Last(FirstN(Topic.WorkdayResponseRecord.LastName, Value)).Value,
+ CompanyCode: Last(FirstN(Topic.WorkdayResponseRecord.CompanyCode, Value)).Value,
+ CompanyName: Last(FirstN(Topic.WorkdayResponseRecord.CompanyName, Value)).Value
+ }
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_yepfHF
+ conditions:
+ - id: conditionItem_86c1ij
+ condition: =!IsBlank(Topic.EmployeeName)
+ displayName: Check if EmployeeName provided
+ actions:
+ - kind: SetVariable
+ id: setVariable_z1fKB1
+ displayName: Filter for the given EmployeeName
+ variable: Topic.workdayResponseTable
+ value: |
+ =Filter(
+ Topic.workdayResponseTable,
+ FirstName = Topic.EmployeeName Or
+ LastName = Topic.EmployeeName Or
+ Concatenate(FirstName, " ", LastName) = Topic.EmployeeName Or
+ Concatenate(LastName, " ", FirstName) = Topic.EmployeeName
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_BVYTlV
+ conditions:
+ - id: conditionItem_QmVzq2
+ condition: =IsEmpty(Topic.workdayResponseTable)
+ displayName: Check is filtered data empty
+ actions:
+ - kind: SendActivity
+ id: SN9frD
+ displayName: Respond with no access message
+ activity: It looks like you don't have access to this information. Try making a new request.
+
+ - kind: SetVariable
+ id: setVariable_wd0UBE
+ displayName: Clear workday response
+ variable: Topic.workdayResponse
+ value: =""
+
+ - kind: EndDialog
+ id: xVALae
+
+inputType:
+ properties:
+ EmployeeName:
+ displayName: EmployeeName
+ description: Employee name whose company code needs to be fetched
+ type: String
+
+outputType:
+ properties:
+ workdayResponseTable:
+ displayName: workdayResponseTable
+ type:
+ kind: Table
+ properties:
+ CompanyCode: String
+ CompanyName: String
+ FirstName: String
+ LastName: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-CostCenter/msdyn_HRWorkdayHCMManagerDirectCostCenter.xml b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-CostCenter/msdyn_HRWorkdayHCMManagerDirectCostCenter.xml
new file mode 100644
index 00000000..291556b8
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-CostCenter/msdyn_HRWorkdayHCMManagerDirectCostCenter.xml
@@ -0,0 +1,77 @@
+
+
+
+
+ User
+
+ Tempalte_ManagerDirectCostCenter_GetWorkersManagerRequest
+ Human_Resources
+ v42.0
+
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Worker_ID']/text()
+ WorkerID
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']
+ LegalNameData
+
+
+ //*[local-name()='Worker_Organization_Data'][*[local-name()='Organization_Data']/*[local-name()='Organization_Type_Reference']/*[local-name()='ID'][@*[local-name()='type']='Organization_Type_ID' and text()='Cost_Center']]/*[local-name()='Organization_Data']/*[local-name()='Organization_Code']/text()
+ CostCenterCode
+
+
+ //*[local-name()='Worker_Organization_Data'][*[local-name()='Organization_Data']/*[local-name()='Organization_Type_Reference']/*[local-name()='ID'][@*[local-name()='type']='Organization_Type_ID' and text()='Cost_Center']]/*[local-name()='Organization_Data']/*[local-name()='Organization_Name']/text()
+ CostCenterName
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Job_Data']/*[local-name()='Position_Data']/*[local-name()='Position_ID']/text()
+ PositionID
+
+
+
+
+
+
+
+
+
+
+ {ManagerOrgId}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+ false
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-CostCenter/topic.yaml b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-CostCenter/topic.yaml
new file mode 100644
index 00000000..f22dfda5
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-CostCenter/topic.yaml
@@ -0,0 +1,158 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ You will respond to requests about the cost center of direct reports of the user making the request. Your information is retrieved from Workday, and will only contain results regarding the user's direct reports. There is no information available for anyone who isn't a direct report, and being a direct report means that the individual is not a manager, spouse, sibling, or any other relationship to the requestor, and only means that they report to the requestor. The resulting data will contain a list of employees who report to the requestor and will contain their company name and cost center. You must NOT give data if you do not have enough info.
+
+ Example invalid requests:
+ "What is my manager's cost center?"
+ "What is my sister's cost center?"
+
+ Example valid request:
+ "What is the cost center of my direct reports?"
+
+ Your output **must** be a nested list in markdown language based on the data contained in the {Topic.workdayResponseTable} variable
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: EmployeeName
+ description: Employee name whose cost center needs to be retrieved
+ entity: PersonNamePrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - Show me my team’s cost center data?
+ - What cost centers are assigned to my reports?
+ - What are my team’s cost centers?
+
+ actions:
+ - kind: BeginDialog
+ id: 3VGa9O
+ displayName: Check manager status
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdayManagerCheck
+
+ - kind: BeginDialog
+ id: jJpz3n
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{ManagerOrgId}"",""value"":""" & Global.ESS_UserContext_ManagerOrganizationId & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMManagerDirectCostCenter
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: aXVFNu
+ displayName: Parse value to a record
+ variable: Topic.workdayResponseRecord
+ valueType:
+ kind: Record
+ properties:
+ CostCenterCode:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ CostCenterName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ LegalNameData:
+ type:
+ kind: Table
+ properties:
+ Name_Detail_Data:
+ type:
+ kind: Record
+ properties:
+ @Formatted_Name: String
+ First_Name: String
+ Last_Name: String
+
+ WorkerID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: setVariable_ztXmui
+ displayName: Extract data to table
+ variable: Topic.workdayResponseTable
+ value: |-
+ =ForAll(
+ Sequence(CountRows(Topic.workdayResponseRecord.WorkerID)),
+ {
+ FirstName: Last(FirstN(Topic.workdayResponseRecord.LegalNameData, Value)).Name_Detail_Data.First_Name,
+ LastName: Last(FirstN(Topic.workdayResponseRecord.LegalNameData, Value)).Name_Detail_Data.Last_Name,
+ CostCenterCode: Last(FirstN(Topic.workdayResponseRecord.CostCenterCode, Value)).Value,
+ CostCenterName: Last(FirstN(Topic.workdayResponseRecord.CostCenterName, Value)).Value
+ }
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_eEjEWp
+ conditions:
+ - id: conditionItem_yv6Ggl
+ condition: =!IsBlank(Topic.EmployeeName)
+ displayName: Check if EmployeeName provided
+ actions:
+ - kind: SetVariable
+ id: setVariable_njPUra
+ displayName: Filter for the given EmployeeName
+ variable: Topic.workdayResponseTable
+ value: =Filter(Topic.workdayResponseTable,'FirstName' = Topic.EmployeeName Or 'LastName' = Topic.EmployeeName Or Concatenate('FirstName', " ", 'LastName') = Topic.EmployeeName Or Concatenate('LastName', " ", 'FirstName') = Topic.EmployeeName)
+
+ - kind: ConditionGroup
+ id: conditionGroup_tAWzhS
+ conditions:
+ - id: conditionItem_sV8WXF
+ condition: =IsEmpty(Topic.workdayResponseTable)
+ displayName: Check is filtered data empty
+ actions:
+ - kind: SendActivity
+ id: sendActivity_3owxH6
+ displayName: Respond with no access message
+ activity: It looks like you don't have access to this information. Try making a new request.
+
+ - kind: SetVariable
+ id: setVariable_wd0UBE
+ displayName: Clear workday response
+ variable: Topic.workdayResponse
+ value: =""
+
+ - kind: EndDialog
+ id: HsUkV7
+
+inputType:
+ properties:
+ EmployeeName:
+ displayName: EmployeeName
+ description: Employee name whose cost center needs to be retrieved
+ type: String
+
+outputType:
+ properties:
+ workdayResponseTable:
+ displayName: workdayResponseTable
+ type:
+ kind: Table
+ properties:
+ CostCenterCode: String
+ CostCenterName: String
+ FirstName: String
+ LastName: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-Jobtaxanomy/msdyn_HRWorkdayHCMManagerJobTaxonomy.xml b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-Jobtaxanomy/msdyn_HRWorkdayHCMManagerJobTaxonomy.xml
new file mode 100644
index 00000000..a447ffeb
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-Jobtaxanomy/msdyn_HRWorkdayHCMManagerJobTaxonomy.xml
@@ -0,0 +1,77 @@
+
+
+
+
+ User
+
+ msdyn_HRWorkdayHCMManagerJobTaxonomy_GetWorkersManagerRequest
+ Human_Resources
+ v41.0
+
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Worker_ID']/text()
+ WorkerID
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/*[local-name()='First_Name']/text()
+
+ FirstName
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/*[local-name()='Last_Name']/text()
+ LastName
+
+
+ //*[local-name()="Position_Title"]/text()
+ JobTitle
+
+
+ //*[local-name()="Business_Title"]/text()
+ BusinessTitle
+
+
+ //*[local-name()="Job_Profile_Name"]/text()
+ JobProfile
+
+
+ //*[local-name()='Job_Profile_Summary_Data']/*[local-name()='Job_Family_Reference']/*[local-name()='ID' and @*[local-name()='type']='Job_Family_ID']/text()
+ JobFamilyId
+
+
+
+
+
+
+
+
+
+
+ {ManagerOrgId}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-Jobtaxanomy/topic.yaml b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-Jobtaxanomy/topic.yaml
new file mode 100644
index 00000000..f3eb009a
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/ManagerScenarios/WorkdayManagersdirect-Jobtaxanomy/topic.yaml
@@ -0,0 +1,203 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ You will respond to requests about the job function of direct reports of the user making the request. Your information is retrieved from Workday, and will only contain results regarding the user's direct reports. There is no information available for anyone who isn't a direct report, and being a direct report means that the individual is not a manager, spouse, sibling, or any other relationship to the requestor, and only means that they report to the requestor. The resulting data will contain a list of employees who report to the requestor and will contain their job function data. You must NOT give data if you do not have enough info.
+
+ Example invalid requests:
+ "What is my manager's job function?"
+ "What is my sister's job title?"
+
+ Example valid requests:
+ "What is the external title of my direct reports?"
+ "What is [EmployeeName]'s job title?"
+
+ Your output **must** be a nested list in markdown language based on the data contained in the {Topic.workdayResponseTable} variable.
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: EmployeeName
+ description: Employee name whose job data needs to be retrieved
+ entity: PersonNamePrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - Show me my team's job title?
+ - What is the internal title of my direct report X?
+ - What is the job function of my team?
+ - What job profile is mapped to my team members?
+ - What are the external titles of my team members?
+ - What is the internal title of my direct report [EmployeeName]?
+ - Show me my team's job taxonomy?
+ - What is the job function of my team?
+ - What job profile is mapped to my team members?
+ - What are the external titles of my team members?
+ - Get job data for [EmployeeName].
+ - What is the job title of [EmployeeName]?
+
+ actions:
+ - kind: BeginDialog
+ id: Y8gqWh
+ displayName: Check manager status
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdayManagerCheck
+
+ - kind: BeginDialog
+ id: 7Va4UU
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{ManagerOrgId}"",""value"":""" & Global.ESS_UserContext_ManagerOrganizationId & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMManagerJobTaxonomy
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: mq6jr0
+ displayName: Parse workdayResponse into table
+ variable: Topic.WorkdayResponseRecord
+ valueType:
+ kind: Record
+ properties:
+ BusinessTitle:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ FirstName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ JobFamilyId:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ JobProfile:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ JobTitle:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ LastName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ WorkerID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: BeginDialog
+ id: QdE53O
+ displayName: Refresh JobFamily reference data
+ input:
+ binding:
+ IsTableEmpty: =IsBlank(Global.JobFamilyLookupTable)
+ ReferenceDataKey: Job_Family_ID
+
+ dialog: msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemRefreshReferenceData
+
+ - kind: SetVariable
+ id: setVariable_EFFdlM
+ displayName: Merge all records in table
+ variable: Topic.workdayResponseTable
+ value: |-
+ =ForAll(
+ Sequence(CountRows(Topic.WorkdayResponseRecord.WorkerID)),
+ {
+ FirstName: Last(FirstN(Topic.WorkdayResponseRecord.FirstName, Value)).Value,
+ LastName: Last(FirstN(Topic.WorkdayResponseRecord.LastName, Value)).Value,
+ JobTitle: Last(FirstN(Topic.WorkdayResponseRecord.JobTitle, Value)).Value,
+ BusinessTitle: Last(FirstN(Topic.WorkdayResponseRecord.BusinessTitle, Value)).Value,
+ JobProfile: Last(FirstN(Topic.WorkdayResponseRecord.JobProfile, Value)).Value,
+ JobFamily: LookUp(Global.JobFamilyLookupTable,
+ ID = Last(FirstN(Topic.WorkdayResponseRecord.JobFamilyId, Value)).Value
+ ).Referenced_Object_Descriptor
+ }
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_yepfHF
+ conditions:
+ - id: conditionItem_86c1ij
+ condition: =!IsBlank(Topic.EmployeeName)
+ displayName: Check if EmployeeName provided
+ actions:
+ - kind: SetVariable
+ id: setVariable_z1fKB1
+ displayName: Filter for the given EmployeeName
+ variable: Topic.workdayResponseTable
+ value: |
+ =Filter(
+ Topic.workdayResponseTable,
+ FirstName = Topic.EmployeeName Or
+ LastName = Topic.EmployeeName Or
+ Concatenate(FirstName, " ", LastName) = Topic.EmployeeName Or
+ Concatenate(LastName, " ", FirstName) = Topic.EmployeeName
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_BVYTlV
+ conditions:
+ - id: conditionItem_QmVzq2
+ condition: =IsEmpty(Topic.workdayResponseTable)
+ displayName: Check is filtered data empty
+ actions:
+ - kind: SendActivity
+ id: sendActivity_m8xRtz
+ displayName: Respond with no access message
+ activity: It looks like you don't have access to this information. Try making a new request.
+
+ - kind: SetVariable
+ id: setVariable_wd0UBE
+ displayName: Clear workday response
+ variable: Topic.workdayResponse
+ value: =""
+
+ - kind: EndDialog
+ id: xa93ze
+
+inputType:
+ properties:
+ EmployeeName:
+ displayName: EmployeeName
+ description: Employee name whose job data needs to be retrieved
+ type: String
+
+outputType:
+ properties:
+ workdayResponseTable:
+ displayName: workdayResponseTable
+ type:
+ kind: Table
+ properties:
+ BusinessTitle: String
+ FirstName: String
+ JobFamily: String
+ JobProfile: String
+ JobTitle: String
+ LastName: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/Workday/README.md b/EmployeeSelfServiceAgent/Workday/README.md
new file mode 100644
index 00000000..141c9e0a
--- /dev/null
+++ b/EmployeeSelfServiceAgent/Workday/README.md
@@ -0,0 +1,29 @@
+---
+title: Workday
+parent: Employee Self-Service
+nav_order: 1
+---
+# ESS Workday Scenarios
+
+This folder contains sample topic definitions and ESS Template configurations XML that customers can use to extend the functionality of their ESS Agent setup. Use the topic definitions (`topic.yaml`) and the accompanying Template configuration XML file to create new topics in your environment or to customize the behavior of existing topics for the scenarios listed below.
+
+Usage notes:
+- Each scenario folder contains a `topic.yaml` (the Copilot/ESS topic) and a template configuration used by the topic.
+- Copy `topic.yaml` into your Copilot topic catalog and ensure the template configuration is added to the Employee Self Service Template Configuration.
+- Update parameter bindings (for example employee id, manager org id, effective date) to match your runtime context.
+- The topic `.yaml` files include trigger queries (sample prompts). Use those as seeds for testing.
+
+Below is a consolidated table that lists each scenario, a short description, and sample prompt(s) you can use to test the topic.
+
+| Scenario | Description | Sample prompt(s) |
+|---|---|---|
+| `EmployeeGetVacationBalance` | Returns the requesting user's vacation balance information from Workday. Displays available time off that can be taken. | "What is my vacation balance?"
"How much time off can I take?"
"What is my workday vacation balance?" |
+| `WorkdayEmployeeRequestTimeOff` | Allows employees to submit time off requests for themselves through Workday. Prompts for necessary details like dates and hours. | "Request 8 hours vacation on 2025-09-15"
"I want to request time off"
"Submit vacation request" |
+| `WorkdayEmployeesviewtheirjobtaxonomy` | Responds to requests about the requesting user's job taxonomy (job title, job function, job profile). | "What is my job title?"
"What is my external title?" |
+| `WorkdayGetContactInformation` | Returns the requesting user's contact information (work/home phones, emails, addresses). | "What is my Work Phone?"
"Show my Home Email" |
+| `WorkdayGetEducation` | Returns the requesting user's education history (school, degree, field of study, years attended). | "Show my Education Details"
"What was my field of study?" |
+| `WorkdayGetGovernmentIDs` | Returns government ID information associated with the requesting user's profile (ID types, issued/expiration dates, country). | "What are my Government Ids?" |
+| `WorkdayManagersdirect-CompanyCode` | Returns company code and company name for employees who directly report to the requesting user (manager view). Output is produced as a nested markdown list. | "What are the company codes for my reports?" |
+| `WorkdayManagersdirect-CostCenter` | Returns cost center details for direct reports of the requesting user. Output is produced as a nested markdown list. | "What is the cost center of my direct reports?" |
+| `WorkdayManagersdirect-Jobtaxanomy` | Returns job taxonomy (job title, business title, job profile, job family) for the manager's direct reports. Output is produced as a nested markdown list. | "Show me my team's job title"
"What is the job title of [EmployeeName]?" |
+| `WorkdayManagerServiceAnniversary` | Returns upcoming service anniversaries for a manager's direct reports. The topic returns a markdown table with Employee Name, Hire Date, Upcoming Service Anniversary Date, Upcoming Milestone. | "When are the service anniversaries of all my directs?"
"What is [EmployeeName]'s next service anniversary?" |
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeAddDependents/README.md b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeAddDependents/README.md
new file mode 100644
index 00000000..947322ca
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeAddDependents/README.md
@@ -0,0 +1,104 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Employee Add Dependents
+
+## Overview
+
+This topic enables employees to view their existing dependents and add new dependents to their Workday profile through a conversational interface. Dependents include spouses, domestic partners, children, and other family members who may be covered under employee benefits.
+
+## Features
+
+- View existing dependents with their relationship and date of birth
+- Add new dependents (spouse, child, domestic partner, etc.)
+- Dynamic relationship type dropdown populated from Workday reference data
+- Confirmation flow showing summary of dependent details before submission
+- Form validation for required fields
+
+## Snapshots
+
+
+
+## Trigger Phrases
+
+- "Add a dependent"
+- "I want to add my child as a dependent"
+- "Add my spouse to my benefits"
+- "Register a new dependent"
+- "I need to add a family member"
+- "Show my dependents"
+
+## Files
+
+| File | Description |
+|------|-------------|
+| `topic.yaml` | Copilot Studio topic definition with conversation flow |
+| `msdyn_HRWorkdayHCMEmployeeGetDependents.xml` | XML template for fetching existing dependents |
+| `msdyn_HRWorkdayHCMEmployeeAddDependent.xml` | XML template for adding a new dependent |
+
+## Workday APIs Used
+
+| API | Purpose |
+|-----|---------|
+| `Human_Resources v45.0` | Fetch existing dependents |
+| `Benefits_Administration v45.1` | Add new dependent |
+
+## Flow Overview
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ User Triggers Topic │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Fetch Reference Data (Relationship Types) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Fetch Existing Dependents │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Display Existing Dependents (or "No dependents") │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Show Add Dependent Form (Adaptive Card) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Show Confirmation Card │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Submit to Workday │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Show Success/Error Message │
+└─────────────────────────────────────────────────────────────┘
+```
+
+## Configurations
+
+Environment makers need to configure the following in the topic:
+
+| Configuration | Description | Location in Topic |
+|---------------|-------------|-------------------|
+| **Gender Options** | Configure available gender options (Male, Female, Not_Declared) | Adaptive card dropdown |
+| **Country Codes** | Define available country codes (USA, CAN, GBR, etc.) | Adaptive card dropdown |
+| **Workday Icon** | Update the icon URL to match your organization's branding | Topic properties > Icon |
+| **Workday URL** | Set your organization's Workday tenant URL | HTTP action or connector configuration |
+
+## Dependencies
+
+- **msdyn_HRWorkdayHCMEmployeeGetDependents template**: Required for fetching existing dependents
+- **Employee Context**: Worker ID must be available in the conversation context
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeAddDependents/add_dependents.png b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeAddDependents/add_dependents.png
new file mode 100644
index 00000000..a005d28a
Binary files /dev/null and b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeAddDependents/add_dependents.png differ
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeAddDependents/msdyn_HRWorkdayHCMEmployeeAddDependent.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeAddDependents/msdyn_HRWorkdayHCMEmployeeAddDependent.xml
new file mode 100644
index 00000000..02744f36
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeAddDependents/msdyn_HRWorkdayHCMEmployeeAddDependent.xml
@@ -0,0 +1,65 @@
+
+
+
+
+ User
+
+ Template_AddDependentRequest
+ Benefits_Administration
+ v45.1
+
+
+
+ //*[local-name()='Dependent_Reference']/*[local-name()='ID' and @*[local-name()='type']='WID']/text()
+ DependentWID
+
+
+ //*[local-name()='Dependent_Reference']/*[local-name()='ID' and @*[local-name()='type']='Dependent_ID']/text()
+ DependentID
+
+
+
+
+
+
+
+
+
+ true
+ true
+ true
+
+ Dependent added via Copilot
+
+ {Employee_ID}
+
+
+
+
+
+ {Employee_ID}
+
+
+ {Relationship_Type}
+
+ true
+
+
+
+
+ {Country_Code}
+
+ {First_Name}
+ {Last_Name}
+
+
+ {Date_Of_Birth}
+
+ {Gender}
+
+
+
+
+
+
+
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeAddDependents/msdyn_HRWorkdayHCMEmployeeGetDependents.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeAddDependents/msdyn_HRWorkdayHCMEmployeeGetDependents.xml
new file mode 100644
index 00000000..7a87290b
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeAddDependents/msdyn_HRWorkdayHCMEmployeeGetDependents.xml
@@ -0,0 +1,104 @@
+
+
+
+
+ User
+
+ Template_GetDependentsRequest
+ Human_Resources
+ v42.0
+
+
+
+
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Dependent']/*[local-name()='Dependent_Reference']/*[local-name()='ID' and @*[local-name()='type']='Dependent_ID']/text()
+ DependentID
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Dependent']/*[local-name()='Dependent_Reference']/*[local-name()='ID' and @*[local-name()='type']='WID']/text()
+ DependentWID
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Person_Reference']/*[local-name()='ID' and @*[local-name()='type']='WID']/text()
+ PersonWID
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/@*[local-name()='Formatted_Name']
+ FullName
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/*[local-name()='First_Name']/text()
+ FirstName
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/*[local-name()='Last_Name']/text()
+ LastName
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Personal_Data']/*[local-name()='Personal_Information_Data']/*[local-name()='Birth_Date']/text()
+ DateOfBirth
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Personal_Data']/*[local-name()='Personal_Information_Data']//*[local-name()='Gender_Reference']/*[local-name()='ID' and @*[local-name()='type']='Gender_Code']/text()
+ Gender
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Related_Person_Relationship_Reference']/*[local-name()='ID' and @*[local-name()='type']='Related_Person_Relationship_ID']/text()
+ RelationshipTypeID
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Dependent']/*[local-name()='Dependent_Data']/*[local-name()='Full-time_Student']/text()
+ IsFullTimeStudent
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Dependent']]/*[local-name()='Dependent']/*[local-name()='Dependent_Data']/*[local-name()='Disabled']/text()
+ IsDisabled
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ true
+ false
+ false
+ false
+ false
+ false
+
+
+
+
+
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeAddDependents/topic.yaml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeAddDependents/topic.yaml
new file mode 100644
index 00000000..49d92bc1
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeAddDependents/topic.yaml
@@ -0,0 +1,412 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ Use this topic when the user wants to ADD, REGISTER, or ENROLL a NEW DEPENDENT / family member on THEIR OWN benefits / Workday profile — spouse, domestic partner, child, son, daughter, newborn, baby, stepchild, adopted/foster child, parent, or any dependent eligible under company benefits.
+
+ Trigger phrases:
+ - "Add a dependent"
+ - "Register / enroll a new dependent"
+ - "Add my child / spouse / partner to my benefits"
+ - "Add my newborn / baby / new family member"
+ - "I had a baby / got married / adopted — add them as a dependent"
+ - Life-event changes (marriage, birth, adoption, qualifying life event / QLE) needing a new dependent
+
+ Data is submitted to Workday and belongs exclusively to the requesting user.
+
+ Do NOT trigger for:
+ - Adding another person's dependents
+ - Removing/de-enrolling a dependent (different topic)
+ - Viewing currently listed dependents (different topic)
+ - Updating contact info, beneficiaries, or emergency contacts (different topics)
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent: {}
+
+ actions:
+ # Set Workday URL
+ - kind: SetVariable
+ id: set_workday_url
+ variable: Topic.WorkdayUrl
+ value: https://impl.workday.com//home.htmld
+
+ # Set Workday icon URL
+ - kind: SetVariable
+ id: set_workday_icon_url
+ variable: Topic.WorkdayIconUrl
+ value: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=
+
+ # Intro message
+ - kind: SetVariable
+ id: set_intro_msg_text
+ variable: Topic.introMsgText
+ value: ="Sure, I can help you with that. Here's where you can add a new dependent. You can also add a dependent on [Workday](" & Topic.WorkdayUrl & "), an HR platform your company uses."
+
+ - kind: SendActivity
+ id: intro_msg
+ activity: "{Topic.introMsgText}"
+
+ # Step 1: Fetch reference data for relationship types
+ - kind: ConditionGroup
+ id: checkRelationshipTypes
+ conditions:
+ - id: relationshipTypesUninitialized
+ condition: =IsBlank(Global.RelatedPersonRelationshipLookupTable)
+ displayName: If relationship type picklist is uninitialized
+ actions:
+ - kind: BeginDialog
+ id: fetchRelationshipTypes
+ displayName: Redirect to Workday System Get Reference Data
+ input:
+ binding:
+ referenceDataKey: Related_Person_Relationship_ID
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.GetReferenceData
+
+ # Step 2: Fetch existing dependents
+ - kind: BeginDialog
+ id: getDependents_BeginDialog
+ displayName: Get Current Dependents
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Now(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetDependents
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ConditionGroup
+ id: checkGetSuccess
+ conditions:
+ - id: getSuccessCondition
+ condition: =Topic.isSuccess = false
+ actions:
+ - kind: SendActivity
+ id: sendErrorMessage
+ activity: I encountered an error retrieving your dependent information. Please try again later or contact support.
+ - kind: EndDialog
+ id: endOnError
+
+ # Step 3: Parse the response
+ - kind: ParseValue
+ id: parseDependentsResponse
+ displayName: Parse dependents response
+ variable: Topic.dependentsRecord
+ valueType:
+ kind: Record
+ properties:
+ DependentID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ DependentWID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ PersonWID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ FullName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ FirstName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ LastName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ DateOfBirth:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ Gender:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ RelationshipTypeID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ IsFullTimeStudent:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ IsDisabled:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ value: =Topic.workdayResponse
+
+ # Step 4: Transform to table (XPath already filters to only actual dependents)
+ - kind: SetVariable
+ id: setVariable_transformDependents
+ displayName: Transform dependents to table
+ variable: Topic.dependentsTable
+ value: |-
+ =ForAll(
+ Sequence(CountRows(Topic.dependentsRecord.DependentID)),
+ {
+ DependentID: Last(FirstN(Topic.dependentsRecord.DependentID, Value)).Value,
+ DependentWID: Last(FirstN(Topic.dependentsRecord.DependentWID, Value)).Value,
+ PersonWID: Last(FirstN(Topic.dependentsRecord.PersonWID, Value)).Value,
+ FullName: Last(FirstN(Topic.dependentsRecord.FullName, Value)).Value,
+ FirstName: Last(FirstN(Topic.dependentsRecord.FirstName, Value)).Value,
+ LastName: Last(FirstN(Topic.dependentsRecord.LastName, Value)).Value,
+ DateOfBirth: Last(FirstN(Topic.dependentsRecord.DateOfBirth, Value)).Value,
+ Gender: Last(FirstN(Topic.dependentsRecord.Gender, Value)).Value,
+ RelationshipType: LookUp(Global.RelatedPersonRelationshipLookupTable, ID = Last(FirstN(Topic.dependentsRecord.RelationshipTypeID, Value)).Value).Referenced_Object_Descriptor,
+ RelationshipTypeID: Last(FirstN(Topic.dependentsRecord.RelationshipTypeID, Value)).Value,
+ IsFullTimeStudent: Last(FirstN(Topic.dependentsRecord.IsFullTimeStudent, Value)).Value = "1",
+ IsDisabled: Last(FirstN(Topic.dependentsRecord.IsDisabled, Value)).Value = "1"
+ }
+ )
+
+ # Step 5: Collect dependent information using Adaptive Card
+ - kind: AdaptiveCardPrompt
+ id: collectDependentCard
+ displayName: Collect dependent information
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Add a new dependent",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "TextBlock",
+ text: "Fields marked with * are required.",
+ wrap: true,
+ size: "Small",
+ spacing: "Small"
+ },
+ {
+ type: "Input.Text",
+ id: "firstName",
+ label: "First name",
+ placeholder: "Enter first name",
+ isRequired: true,
+ errorMessage: "First name is required."
+ },
+ {
+ type: "Input.Text",
+ id: "lastName",
+ label: "Last name",
+ placeholder: "Enter last name",
+ isRequired: true,
+ errorMessage: "Last name is required."
+ },
+ {
+ type: "Input.Date",
+ id: "dateOfBirth",
+ label: "Date of birth",
+ isRequired: true,
+ errorMessage: "Date of birth is required."
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "gender",
+ label: "Gender",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a gender.",
+ placeholder: "Select gender",
+ choices: [
+ { title: "Male", value: "Male" },
+ { title: "Female", value: "Female" },
+ { title: "Not declared", value: "Not_Declared" }
+ ]
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "relationshipType",
+ label: "Relationship to employee",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a relationship.",
+ placeholder: "Select relationship",
+ choices: ForAll(Global.RelatedPersonRelationshipLookupTable, { title: ThisRecord.Referenced_Object_Descriptor, value: ThisRecord.ID })
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "country",
+ label: "Country",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a country.",
+ value: "USA",
+ choices: [
+ { title: "United States", value: "USA" },
+ { title: "Canada", value: "CAN" },
+ { title: "United Kingdom", value: "GBR" },
+ { title: "India", value: "IND" },
+ { title: "Australia", value: "AUS" },
+ { title: "Germany", value: "DEU" },
+ { title: "France", value: "FRA" }
+ ]
+ }
+ ],
+ actions: [
+ { type: "Action.Submit", title: "Submit", id: "Submit", data: { actionSubmitId: "Submit" } },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", data: { actionSubmitId: "Cancel" }, associatedInputs: "none" }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.formActionId
+ firstName: Topic.firstName
+ lastName: Topic.lastName
+ dateOfBirth: Topic.dateOfBirth
+ gender: Topic.gender
+ relationshipType: Topic.relationshipType
+ country: Topic.country
+ outputType:
+ properties:
+ actionSubmitId: String
+ firstName: String
+ lastName: String
+ dateOfBirth: String
+ gender: String
+ relationshipType: String
+ country: String
+
+ # Step 7: Handle cancel
+ - kind: ConditionGroup
+ id: checkCancelAction
+ conditions:
+ - id: cancelCondition
+ condition: =Topic.formActionId = "Cancel"
+ actions:
+ - kind: SendActivity
+ id: sendCancelMessage
+ activity: Request cancelled. No dependent was added. Is there anything else I can help you with?
+ - kind: CancelAllDialogs
+ id: cancelAll
+
+ # Step 8: Validate required fields
+ - kind: ConditionGroup
+ id: validateFields
+ conditions:
+ - id: missingFieldsCondition
+ condition: =IsBlank(Topic.firstName) || IsBlank(Topic.lastName) || IsBlank(Topic.dateOfBirth) || IsBlank(Topic.gender) || IsBlank(Topic.relationshipType) || IsBlank(Topic.country)
+ actions:
+ - kind: SendActivity
+ id: sendValidationError
+ activity: Please fill in all required fields (first name, last name, date of birth, gender, relationship, and country).
+ - kind: EndDialog
+ id: endOnValidationError
+
+ # Step 9: Set relationship type ID (already comes from lookup table)
+ - kind: SetVariable
+ id: mapRelationshipType
+ displayName: Set relationship type ID
+ variable: Topic.relationshipTypeId
+ value: =Topic.relationshipType
+
+ # Step 10: Call Add Dependent API
+ - kind: BeginDialog
+ id: addDependent_BeginDialog
+ displayName: Add Dependent to Workday
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{First_Name}"",""value"":""" & Topic.firstName & """},{""key"":""{Last_Name}"",""value"":""" & Topic.lastName & """},{""key"":""{Date_Of_Birth}"",""value"":""" & Topic.dateOfBirth & """},{""key"":""{Gender}"",""value"":""" & Topic.gender & """},{""key"":""{Relationship_Type}"",""value"":""" & Topic.relationshipTypeId & """},{""key"":""{Country_Code}"",""value"":""" & Topic.country & """}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeAddDependent
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.addErrorResponse
+ isSuccess: Topic.addIsSuccess
+ workdayResponse: Topic.addWorkdayResponse
+
+ # Step 11: Show result
+ - kind: ConditionGroup
+ id: checkAddSuccess
+ conditions:
+ - id: addSuccessCondition
+ condition: =Topic.addIsSuccess = true
+ actions:
+ - kind: SendActivity
+ id: sendSuccessIntroMessage
+ activity: Great! You've added a new dependent.
+
+ - kind: SetVariable
+ id: setOtherDependentsList
+ variable: Topic.otherDependentsList
+ value: =If(CountRows(Topic.dependentsTable) > 0, Concat(Topic.dependentsTable, FullName, ", "), "None")
+
+ - kind: SendActivity
+ id: sendSuccessMessage
+ activity:
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "✅ Your new dependent was added",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "FactSet",
+ spacing: "Medium",
+ facts: [
+ { title: "Name", value: Topic.firstName & " " & Topic.lastName },
+ { title: "Date of birth", value: Topic.dateOfBirth },
+ { title: "Gender", value: Topic.gender },
+ { title: "Relationship to employee", value: LookUp(Global.RelatedPersonRelationshipLookupTable, ID = Topic.relationshipType).Referenced_Object_Descriptor },
+ { title: "Other dependents", value: Topic.otherDependentsList }
+ ]
+ }
+ ]
+ }
+
+ - kind: CancelAllDialogs
+ id: endOnSuccess
+
+ elseActions:
+ - kind: SendActivity
+ id: sendAddErrorMessage
+ activity: |-
+ There was an error adding the dependent. Please try again later or contact HR support.
+
+ **Error details:** {Topic.addErrorResponse}
+
+ - kind: CancelAllDialogs
+ id: endOnError2
+
+inputType: {}
+outputType:
+ properties:
+ addIsSuccess:
+ displayName: Add Success
+ type: Boolean
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeGetVacationBalance/README.md b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeGetVacationBalance/README.md
new file mode 100644
index 00000000..9dee802a
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeGetVacationBalance/README.md
@@ -0,0 +1,98 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Get Vacation Balance
+
+## Overview
+
+This scenario allows employees to check their time off balances from Workday. It retrieves vacation, sick leave, floating holiday, and other plan balances for the requesting user.
+
+The `EmployeeGetVacationBalance` topic allows employees to:
+- **View** their current time off balances across all plans
+- **Select** a specific as-of date to check historical or future balances
+- **Receive** AI-formatted responses with balance details
+
+## Features
+
+- **Date Selection**: Prompts user to select an as-of date via Adaptive Card, defaults to today
+- **Multiple Plan Support**: Returns balances for all time off plans (vacation, sick, floating holiday, etc.)
+- **AI-Formatted Response**: Uses generative AI to format the balance response in a friendly, conversational way
+- **Automatic Date Extraction**: If user includes a date in their request, it's automatically extracted
+
+## Snapshots
+
+### Date Selection Card
+
+
+## Trigger Phrases
+
+- "What is my vacation balance?"
+- "How much time off can I take?"
+- "What is my Workday vacation balance?"
+- "Check my PTO balance"
+- "How many sick days do I have left?"
+- "Show me my time off balance as of January 1st"
+
+## Files
+
+| File | Description |
+|------|-------------|
+| `topic.yaml` | Main topic definition with conversation flow and Adaptive Card |
+| `msdyn_HRWorkdayHCMEmployeeGetVacationBalance.xml` | XML template for the Get_Time_Off_Plan_Balances API request |
+
+## Workday APIs Used
+
+| API | Service | Version | Purpose |
+|-----|---------|---------|---------|
+| `Get_Time_Off_Plan_Balances` | Absence_Management | v42.0 | Retrieve employee's time off plan balances |
+
+## Flow Overview
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ User Triggers Topic │
+│ "What is my vacation balance?" │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+ ┌───────────────┴───────────────┐
+ │ Date provided in request? │
+ └───────────────┬───────────────┘
+ │ │
+ Yes No
+ │ │
+ ▼ ▼
+ ┌───────────────────────┐ ┌─────────────────────┐
+ │ Use extracted date │ │ Show Date Picker │
+ │ │ │ Adaptive Card │
+ └───────────────────────┘ │ (defaults to today)│
+ │ └──────────┬──────────┘
+ │ │
+ └────────────┬───────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Call Workday API │
+│ (Get_Time_Off_Plan_Balances) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ AI Formats Response │
+│ (Friendly message with balances by plan) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Display to User │
+│ "As of [date], here are your balances: │
+│ • Vacation: X hours │
+│ • Sick Leave: Y hours" │
+└─────────────────────────────────────────────────────────────┘
+```
+
+## Dependencies
+
+- `msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution` - For API execution
+- `Global.ESS_UserContext_Employee_Id` - Current user's employee ID
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeGetVacationBalance/get_vacation_balance.png b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeGetVacationBalance/get_vacation_balance.png
new file mode 100644
index 00000000..4d53f446
Binary files /dev/null and b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeGetVacationBalance/get_vacation_balance.png differ
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeGetVacationBalance/msdyn_HRWorkdayHCMEmployeeGetVacationBalance.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeGetVacationBalance/msdyn_HRWorkdayHCMEmployeeGetVacationBalance.xml
new file mode 100644
index 00000000..ab2b9b4f
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeGetVacationBalance/msdyn_HRWorkdayHCMEmployeeGetVacationBalance.xml
@@ -0,0 +1,85 @@
+
+
+
+
+ User
+
+ msdyn_HRWorkdayHCMEmployeeGetVacationBalance
+ Absence_Management
+ v42.0
+
+
+
+
+ Employee_ID
+ {Employee_ID}
+
+
+ As_Of_Effective_Date
+ {As_Of_Effective_Date}
+
+
+
+
+
+
+ RemainingBalance
+
+ //*[local-name()='Time_Off_Plan_Balance_Record']
+ /*[local-name()='Time_Off_Plan_Balance_Position_Record']
+ /*[local-name()='Time_Off_Plan_Balance']/text()
+
+
+
+
+
+ PlanDescriptor
+
+ //*[local-name()='Time_Off_Plan_Balance_Record']
+ /*[local-name()='Time_Off_Plan_Reference']/@*[local-name()='Descriptor']
+
+
+
+
+
+ PlanID
+
+ //*[local-name()='Time_Off_Plan_Balance_Record']
+ /*[local-name()='Time_Off_Plan_Reference']
+ /*[local-name()='ID'][@*[local-name()='type']='Absence_Plan_ID']/text()
+
+
+
+
+
+ UnitOfTime
+
+ //*[local-name()='Time_Off_Plan_Balance_Record']
+ /*[local-name()='Unit_of_Time_Reference']/@*[local-name()='Descriptor']
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+
+ {As_Of_Effective_Date}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeGetVacationBalance/topic.yaml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeGetVacationBalance/topic.yaml
new file mode 100644
index 00000000..d22efca8
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeGetVacationBalance/topic.yaml
@@ -0,0 +1,150 @@
+kind: AdaptiveDialog
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: AsOfEffectiveDate
+ description: The as-of effective date (YYYY-MM-DD) to use when retrieving vacation balances. If the user includes a date in their prompt, the NLU should populate this automatically.
+ entity: DateTimePrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+modelDescription: |-
+ Use this topic when the user asks about THEIR OWN time off balance / leave balance / vacation balance / PTO balance / remaining days / accrued hours stored in Workday — vacation, PTO (paid time off), annual leave, sick leave, sick days, personal days, personal time, floating holidays, comp time, accrued hours, remaining days, carryover, or any leave-type balance.
+
+ Triggers: "What's my vacation balance?", "How many PTO days do I have left?", "How much sick leave do I have?", "Show my time off balance", "Remaining vacation days", "Accrued hours", "How many days off do I have?", "What's my carryover?".
+
+ Data belongs exclusively to the requesting user.
+
+ Do NOT trigger for:
+ - REQUESTING / submitting time off (use the RequestTimeOff topic)
+ - Cancelling or modifying a submitted leave request
+ - General questions about leave policy or accrual rules
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - What is my vacation balance?
+ - How much time off can I take?
+ - what is my workday vacation balance?
+
+ actions:
+ - kind: SetVariable
+ id: set_workday_url
+ variable: Topic.WorkdayUrl
+ value: https://impl.workday.com//home.htmld
+
+ - kind: SetVariable
+ id: set_intro_msg_text
+ variable: Topic.introMsgText
+ value: ="Sure, here's what I found on your time off balance from [Workday](" & Topic.WorkdayUrl & "), an HR platform your company uses."
+
+ - kind: SendActivity
+ id: intro_msg
+ activity: "{Topic.introMsgText}"
+
+ - kind: SetVariable
+ id: setVariable_8qSSM1
+ variable: Topic.date
+ value: =Text(Today(), "yyyy-MM-dd")
+
+ - kind: ConditionGroup
+ id: ask_effective_date_if_blank
+ conditions:
+ - id: isBlank
+ condition: =IsBlank(Topic.AsOfEffectiveDate)
+ actions:
+ - kind: AdaptiveCardPrompt
+ id: askDateCard1
+ displayName: Select As-Of Date
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "https://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Which date would you like to check your vacation balance for?",
+ weight: "Bolder",
+ wrap: true
+ },
+ {
+ type: "Input.Date",
+ id: "asOfDate",
+ label: "Select date",
+ value: Topic.date
+ }
+ ],
+ actions: [
+ {
+ type: "Action.Submit",
+ title: "Submit"
+ }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.actionSubmitId
+ asOfDate: Topic.asOfDate
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ asOfDate: Date
+
+ - kind: SetVariable
+ id: ensure_effective_date2
+ variable: Topic.AsOfEffectiveDate
+ value: =If(IsBlank(Topic.AsOfEffectiveDate), DateTimeValue(Topic.asOfDate), Topic.AsOfEffectiveDate)
+
+ - kind: BeginDialog
+ id: Y74Om43
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Topic.AsOfEffectiveDate, "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetVacationBalance
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: AnswerQuestionWithAI
+ id: igank32
+ autoSend: false
+ variable: Topic.VacationBalance
+ userInput: =Topic.workdayResponse
+ additionalInstructions: Do not show citation. Display each vacation balances in a separate line and highlight vacation name. Only reply back with the Vacation Balance. Format the response in in friendly way and make sure to tie it back directly to the orginal question of the user. ({System.Activity.Text}). Display ({Topic.asOfDate}) with the message as of. Never include the "Plan ID".
+
+ - kind: SendActivity
+ id: sendActivity_56apKp
+ activity: |-
+ Here is your vacation balance:
+ {Topic.VacationBalance}
+
+inputType:
+ properties:
+ AsOfEffectiveDate:
+ displayName: AsOfEffectiveDate
+ description: The effective date to use when retrieving vacation balances (YYYY-MM-DD). Defaults to today's date if not provided.
+ type: DateTime
+
+outputType:
+ properties:
+ finalizedData:
+ displayName: finalizedData
+ type:
+ kind: Record
+ properties:
+ CostCenterCode: String
+ CostCenterName: String
+ EmployeeName: String
+
+ VacationBalance:
+ displayName: VacationBalance
+ type: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/README.md b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/README.md
new file mode 100644
index 00000000..248e2578
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/README.md
@@ -0,0 +1,93 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Employee Update Residential Address
+
+## Overview
+
+This topic enables employees to manage their home/residential address in Workday through the Copilot agent. It retrieves the employee's current home addresses, allows them to select which one to update, add a new address, or modify an existing one, and submits the changes via the Workday Change Home Contact Information API.
+
+## Features
+
+- View current home addresses with primary address marked
+- Update any field of an existing home address
+- Add a new home address when no addresses exist or user wants to add another
+- Set or change which address is the primary home address
+- Form pre-population with current address values
+
+## Snapshots
+
+
+
+## Trigger Phrases
+
+- "Update my home address"
+- "I want to update my residential address"
+- "Change my address"
+- "Update my street address"
+- "I moved to a new address"
+
+## Files
+
+| File | Description |
+|------|-------------|
+| `topic.yaml` | Copilot Studio topic definition with conversation flow |
+| `msdyn_GetResidentialAddress_Template.xml` | XML template to retrieve current home addresses |
+| `msdyn_UpdateResidentialAddress_Template.xml` | XML template to update an existing home address |
+| `msdyn_AddResidentialAddress_Template.xml` | XML template to add a new home address |
+
+## Workday APIs Used
+
+| API | Purpose |
+|-----|---------|
+| `Get_Workers` | Retrieve current home address information |
+| `Change_Home_Contact_Information` | Add or update home address |
+
+## Flow Overview
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ User Triggers Topic │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Fetch Current Home Addresses │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Show Selection (Multiple) or Auto-Select (Single) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Collect New Address via Adaptive Card Form │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Submit to Workday │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Show Success/Error Message │
+└─────────────────────────────────────────────────────────────┘
+```
+
+## Configurations
+
+Environment makers need to configure the following in the topic:
+
+| Configuration | Description | Location in Topic |
+|---------------|-------------|-------------------|
+| **Countries** | Available country options (USA, CAN, GBR, etc.) | Adaptive card dropdown |
+| **States/Provinces** | Available state/province codes per country | Adaptive card dropdown |
+| **Workday Icon** | Update the icon URL to match your organization's branding | Topic properties > Icon |
+| **Workday URL** | Set your organization's Workday tenant URL | HTTP action or connector configuration |
+
+## Dependencies
+
+- **Employee Context**: Worker ID must be available in the conversation context
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_HRWorkdayHCMEmployeeAddResidentialAddress.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_HRWorkdayHCMEmployeeAddResidentialAddress.xml
new file mode 100644
index 00000000..74699815
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_HRWorkdayHCMEmployeeAddResidentialAddress.xml
@@ -0,0 +1,69 @@
+
+
+
+
+ User
+
+ Template_AddResidentialAddressRequest
+ Human_Resources
+ v42.0
+
+
+
+ //*[local-name()='Event_Reference']/*[local-name()='ID' and @*[local-name()='type']='WID']/text()
+ EventWID
+
+
+
+
+
+
+
+
+
+ false
+ true
+ true
+
+ New address added via Copilot
+
+ {Employee_ID}
+
+
+
+
+
+ {Employee_ID}
+
+ {Event_Effective_Date}
+
+
+
+
+
+ {Country_Code}
+
+
+ {State_Province_Code}
+
+ {Address_Line_1}
+ {Address_Line_2}
+ {Postal_Code}
+ {City}
+
+
+
+
+ HOME
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_HRWorkdayHCMEmployeeGetResidentialAddress.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_HRWorkdayHCMEmployeeGetResidentialAddress.xml
new file mode 100644
index 00000000..e3424dc9
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_HRWorkdayHCMEmployeeGetResidentialAddress.xml
@@ -0,0 +1,106 @@
+
+
+
+
+ User
+
+ Template_GetResidentialAddressRequest
+ Human_Resources
+ v42.0
+
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/@*[local-name()='Formatted_Address']
+ FormattedAddress
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Address_ID']/text()
+ AddressID
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Country_Reference']/*[local-name()='ID' and @*[local-name()='type']='ISO_3166-1_Alpha-3_Code']/text()
+ CountryCode
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Country_Region_Reference']/*[local-name()='ID' and @*[local-name()='type']='Country_Region_ID']/text()
+ StateProvinceCode
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Municipality']/text()
+ City
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Postal_Code']/text()
+ PostalCode
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Address_Line_Data' and @*[local-name()='Type']='ADDRESS_LINE_1']/text()
+ AddressLine1
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Address_Line_Data' and @*[local-name()='Type']='ADDRESS_LINE_2']/text()
+ AddressLine2
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Usage_Data']/*[local-name()='Type_Data']/@*[local-name()='Primary']
+ IsPrimary
+
+
+ //*[local-name()='Contact_Data']/*[local-name()='Address_Data']/*[local-name()='Usage_Data']/*[local-name()='Type_Data']/*[local-name()='Type_Reference']/*[local-name()='ID' and @*[local-name()='type']='Communication_Usage_Type_ID']/text()
+ UsageType
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+ true
+ false
+ false
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+
+
+
+
+
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_HRWorkdayHCMEmployeeUpdateResidentialAddress.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_HRWorkdayHCMEmployeeUpdateResidentialAddress.xml
new file mode 100644
index 00000000..6110c982
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/msdyn_HRWorkdayHCMEmployeeUpdateResidentialAddress.xml
@@ -0,0 +1,71 @@
+
+
+
+
+ User
+
+ Template_UpdateResidentialAddressRequest
+ Human_Resources
+ v42.0
+
+
+
+ //*[local-name()='Event_Reference']/*[local-name()='ID' and @*[local-name()='type']='WID']/text()
+ EventWID
+
+
+
+
+
+
+
+
+
+ false
+ true
+ true
+
+ Address updated via Copilot
+
+ {Employee_ID}
+
+
+
+
+
+ {Employee_ID}
+
+ {Event_Effective_Date}
+
+
+
+
+
+ {Country_Code}
+
+
+ {State_Province_Code}
+
+ {Address_Line_1}
+ {Address_Line_2}
+ {Postal_Code}
+ {City}
+
+
+
+
+ HOME
+
+
+
+
+ {Address_ID}
+
+
+
+
+
+
+
+
+
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/topic.yaml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/topic.yaml
new file mode 100644
index 00000000..50f5abaa
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/topic.yaml
@@ -0,0 +1,698 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ You will respond only to requests related to to update, change, edit, modify, or correct the requesting user's own residential/home address in Workday.
+
+ TRIGGER THIS when user wants to: update/change/fix/correct their home address, report they moved or relocated, fix a wrong address on file, change street/city/state/zip
+ code.
+
+ All address data is retrieved from and updated through Workday, and pertains exclusively to the requesting user.
+
+ Do not respond about other people's data.
+
+ Example valid requests:
+ "Update my home address"
+ "I want to update my residential address"
+ "Change my address"
+ "Update my street address"
+ "I moved to a new address"
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent: {}
+ actions:
+ - kind: BeginDialog
+ id: getAddress_BeginDialog
+ displayName: Get Current Residential Address
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Now(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetResidentialAddress
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ConditionGroup
+ id: checkGetSuccess
+ conditions:
+ - id: getSuccessCondition
+ condition: =Topic.isSuccess = false
+ actions:
+ - kind: SendActivity
+ id: sendErrorMessage
+ activity: I encountered an error retrieving your current address. Please try again later or contact support.
+
+ - kind: EndDialog
+ id: endOnError
+
+ - kind: ParseValue
+ id: parseAddressResponse
+ displayName: Parse address response
+ variable: Topic.addressRecord
+ valueType:
+ kind: Record
+ properties:
+ AddressID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ AddressLine1:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ AddressLine2:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ City:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ CountryCode:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ FormattedAddress:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ IsPrimary:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ PostalCode:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ StateProvinceCode:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ UsageType:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: setVariable_transformAddresses
+ displayName: Transform addresses to table
+ variable: Topic.addressTable
+ value: |-
+ =ForAll(
+ Sequence(CountRows(Topic.addressRecord.AddressID)),
+ {
+ AddressID: Last(FirstN(Topic.addressRecord.AddressID, Value)).Value,
+ FormattedAddress: Last(FirstN(Topic.addressRecord.FormattedAddress, Value)).Value,
+ CountryCode: Last(FirstN(Topic.addressRecord.CountryCode, Value)).Value,
+ StateProvinceCode: Last(FirstN(Topic.addressRecord.StateProvinceCode, Value)).Value,
+ City: Last(FirstN(Topic.addressRecord.City, Value)).Value,
+ PostalCode: Last(FirstN(Topic.addressRecord.PostalCode, Value)).Value,
+ AddressLine1: Last(FirstN(Topic.addressRecord.AddressLine1, Value)).Value,
+ AddressLine2: Last(FirstN(Topic.addressRecord.AddressLine2, Value)).Value,
+ IsPrimary: If(Last(FirstN(Topic.addressRecord.IsPrimary, Value)).Value = "1", true, false),
+ UsageType: Last(FirstN(Topic.addressRecord.UsageType, Value)).Value
+ }
+ )
+
+ - kind: SetVariable
+ id: setVariable_homeAddresses
+ displayName: Filter to HOME addresses only
+ variable: Topic.homeAddresses
+ value: =Filter(Topic.addressTable, UsageType = "HOME")
+
+ - kind: ConditionGroup
+ id: checkExistingAddresses
+ conditions:
+ - id: noAddressesCondition
+ condition: =CountRows(Topic.homeAddresses) = 0
+ actions:
+ - kind: SendActivity
+ id: sendNoAddressMessage
+ activity: I don't see a home address on file for you. Let's add a new one.
+
+ - kind: SetVariable
+ id: setIsNewAddress
+ variable: Topic.isNewAddress
+ value: =true
+
+ - kind: SetVariable
+ id: setSelectedAddressID_New
+ variable: Topic.selectedAddressID
+ value: =""
+
+ elseActions:
+ - kind: SetVariable
+ id: setIsExistingAddress
+ variable: Topic.isNewAddress
+ value: =false
+
+ - kind: ConditionGroup
+ id: checkSingleOrMultipleAddresses
+ conditions:
+ - id: singleAddressCondition
+ condition: =CountRows(Topic.homeAddresses) = 1
+ displayName: Single address - auto-select
+ actions:
+ - kind: SetVariable
+ id: autoSelectAddress
+ displayName: Auto-select single address
+ variable: Topic.selectedAddress
+ value: =First(Topic.homeAddresses)
+
+ - kind: SetVariable
+ id: autoSetAddressID
+ variable: Topic.selectedAddressID
+ value: =Topic.selectedAddress.AddressID
+
+ - kind: SendActivity
+ id: sendCurrentAddressMsg
+ activity: |-
+ I found your current home address:
+
+ 📍 **{Topic.selectedAddress.FormattedAddress}**
+
+ elseActions:
+ - kind: SetVariable
+ id: buildAddressChoices
+ displayName: Build address selection choices
+ variable: Topic.addressChoices
+ value: |
+ =ForAll(
+ Topic.homeAddresses,
+ {
+ title: ThisRecord.AddressLine1 & ", " & ThisRecord.City & ", " & ThisRecord.StateProvinceCode & " " & ThisRecord.PostalCode & " (" & If(ThisRecord.IsPrimary, "Primary", "Home") & ")",
+ value: ThisRecord.AddressID
+ }
+ )
+
+ - kind: AdaptiveCardPrompt
+ id: selectAddressCard
+ displayName: Select address to update
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Select an address to update",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "TextBlock",
+ text: "You have " & CountRows(Topic.homeAddresses) & " addresses. Select to update or add a new address.",
+ wrap: true,
+ spacing: "Small"
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "selectedAddress",
+ label: "Address",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select an address.",
+ choices: ForAll(Topic.addressChoices, { title: ThisRecord.title, value: ThisRecord.value }),
+ value: If(CountRows(Topic.addressChoices) > 0, First(Topic.addressChoices).value, "")
+ }
+ ],
+ actions: [
+ { type: "Action.Submit", title: "Continue", id: "Continue", data: { actionSubmitId: "Continue" } },
+ { type: "Action.Submit", title: "Add an address", id: "AddNew", data: { actionSubmitId: "AddNew" }, associatedInputs: "none" },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", data: { actionSubmitId: "Cancel" }, associatedInputs: "none" }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.selectionActionId
+ selectedAddress: Topic.selectedAddressID
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ selectedAddress: String
+
+ - kind: ConditionGroup
+ id: handleSelectionCancel
+ conditions:
+ - id: selectionCancelled
+ condition: =Topic.selectionActionId = "Cancel"
+ actions:
+ - kind: SendActivity
+ id: selectionCancelMsg
+ activity: Your request has been cancelled. Is there anything else I can help you with?
+
+ - kind: CancelAllDialogs
+ id: selectionCancelAll
+
+ - kind: ConditionGroup
+ id: handleAddNewAddress
+ conditions:
+ - id: addNewSelected
+ condition: =Topic.selectionActionId = "AddNew"
+ actions:
+ - kind: SetVariable
+ id: setIsNewAddressFromSelection
+ variable: Topic.isNewAddress
+ value: =true
+
+ - kind: SetVariable
+ id: clearSelectedAddressID
+ variable: Topic.selectedAddressID
+ value: =""
+
+ - kind: ConditionGroup
+ id: checkIfContinueSelected
+ conditions:
+ - id: continueSelected
+ condition: =Topic.selectionActionId = "Continue"
+ actions:
+ - kind: SetVariable
+ id: setSelectedAddress
+ displayName: Set selected address details
+ variable: Topic.selectedAddress
+ value: =LookUp(Topic.homeAddresses, AddressID = Topic.selectedAddressID)
+
+ - kind: ConditionGroup
+ id: prefillExistingAddressFields
+ conditions:
+ - id: isUpdatingExistingAddress
+ condition: =Topic.isNewAddress = false
+ actions:
+ - kind: SetVariable
+ id: setCurrentAddressLine1
+ variable: Topic.currentAddressLine1
+ value: =Topic.selectedAddress.AddressLine1
+
+ - kind: SetVariable
+ id: setCurrentAddressLine2
+ variable: Topic.currentAddressLine2
+ value: =Topic.selectedAddress.AddressLine2
+
+ - kind: SetVariable
+ id: setCurrentCity
+ variable: Topic.currentCity
+ value: =Topic.selectedAddress.City
+
+ - kind: SetVariable
+ id: setCurrentStateProvince
+ variable: Topic.currentStateProvince
+ value: =Topic.selectedAddress.StateProvinceCode
+
+ - kind: SetVariable
+ id: setCurrentPostalCode
+ variable: Topic.currentPostalCode
+ value: =Topic.selectedAddress.PostalCode
+
+ - kind: SetVariable
+ id: setCurrentCountryCode
+ variable: Topic.currentCountryCode
+ value: =Topic.selectedAddress.CountryCode
+
+ - kind: SetVariable
+ id: setCurrentIsPrimary
+ variable: Topic.currentIsPrimary
+ value: =Topic.selectedAddress.IsPrimary
+
+ elseActions:
+ - kind: SetVariable
+ id: clearCurrentAddressLine1
+ variable: Topic.currentAddressLine1
+ value: =""
+
+ - kind: SetVariable
+ id: clearCurrentAddressLine2
+ variable: Topic.currentAddressLine2
+ value: =""
+
+ - kind: SetVariable
+ id: clearCurrentCity
+ variable: Topic.currentCity
+ value: =""
+
+ - kind: SetVariable
+ id: clearCurrentStateProvince
+ variable: Topic.currentStateProvince
+ value: =""
+
+ - kind: SetVariable
+ id: clearCurrentPostalCode
+ variable: Topic.currentPostalCode
+ value: =""
+
+ - kind: SetVariable
+ id: clearCurrentCountryCode
+ variable: Topic.currentCountryCode
+ value: =""
+
+ - kind: SetVariable
+ id: clearCurrentIsPrimary
+ variable: Topic.currentIsPrimary
+ value: =false
+
+ - kind: ConditionGroup
+ id: shortCircuitOnSelectionCancel
+ conditions:
+ - id: selectionCancelledShortCircuit
+ condition: =Topic.selectionActionId = "Cancel"
+ actions:
+ - kind: EndDialog
+ id: endOnSelectionCancel
+
+ - kind: AdaptiveCardPrompt
+ id: collectAddressCard
+ displayName: Collect new address information
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Update Your Home Address",
+ weight: "Bolder",
+ size: "Large"
+ },
+ {
+ type: "TextBlock",
+ text: "Please enter your new address details:",
+ wrap: true
+ },
+ {
+ type: "Input.Text",
+ id: "addressLine1",
+ label: "Address line 1",
+ placeholder: "Street address",
+ isRequired: true,
+ errorMessage: "Address line 1 is required.",
+ value: If(IsBlank(Topic.currentAddressLine1), "", Topic.currentAddressLine1)
+ },
+ {
+ type: "Input.Text",
+ id: "addressLine2",
+ label: "Address line 2",
+ placeholder: "Apt, Suite, Unit, etc. (optional)",
+ value: If(IsBlank(Topic.currentAddressLine2), "", Topic.currentAddressLine2)
+ },
+ {
+ type: "Input.Text",
+ id: "city",
+ label: "City",
+ placeholder: "City",
+ isRequired: true,
+ errorMessage: "City is required.",
+ value: If(IsBlank(Topic.currentCity), "", Topic.currentCity)
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "country",
+ label: "Country",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a country.",
+ value: If(IsBlank(Topic.currentCountryCode), "USA", Topic.currentCountryCode),
+ choices: [
+ { title: "United States", value: "USA" },
+ { title: "Canada", value: "CAN" },
+ { title: "United Kingdom", value: "GBR" },
+ { title: "Australia", value: "AUS" },
+ { title: "Germany", value: "DEU" },
+ { title: "France", value: "FRA" },
+ { title: "India", value: "IND" },
+ { title: "Japan", value: "JPN" },
+ { title: "Mexico", value: "MEX" },
+ { title: "Brazil", value: "BRA" }
+ ]
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "stateProvince",
+ label: "State/province",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a state/province.",
+ value: If(IsBlank(Topic.currentStateProvince), "", Topic.currentStateProvince),
+ choices: [
+ { title: "Alabama", value: "USA-AL" },
+ { title: "Alaska", value: "USA-AK" },
+ { title: "Arizona", value: "USA-AZ" },
+ { title: "Arkansas", value: "USA-AR" },
+ { title: "California", value: "USA-CA" },
+ { title: "Colorado", value: "USA-CO" },
+ { title: "Connecticut", value: "USA-CT" },
+ { title: "Delaware", value: "USA-DE" },
+ { title: "Florida", value: "USA-FL" },
+ { title: "Georgia", value: "USA-GA" },
+ { title: "Hawaii", value: "USA-HI" },
+ { title: "Idaho", value: "USA-ID" },
+ { title: "Illinois", value: "USA-IL" },
+ { title: "Indiana", value: "USA-IN" },
+ { title: "Iowa", value: "USA-IA" },
+ { title: "Kansas", value: "USA-KS" },
+ { title: "Kentucky", value: "USA-KY" },
+ { title: "Louisiana", value: "USA-LA" },
+ { title: "Maine", value: "USA-ME" },
+ { title: "Maryland", value: "USA-MD" },
+ { title: "Massachusetts", value: "USA-MA" },
+ { title: "Michigan", value: "USA-MI" },
+ { title: "Minnesota", value: "USA-MN" },
+ { title: "Mississippi", value: "USA-MS" },
+ { title: "Missouri", value: "USA-MO" },
+ { title: "Montana", value: "USA-MT" },
+ { title: "Nebraska", value: "USA-NE" },
+ { title: "Nevada", value: "USA-NV" },
+ { title: "New Hampshire", value: "USA-NH" },
+ { title: "New Jersey", value: "USA-NJ" },
+ { title: "New Mexico", value: "USA-NM" },
+ { title: "New York", value: "USA-NY" },
+ { title: "North Carolina", value: "USA-NC" },
+ { title: "North Dakota", value: "USA-ND" },
+ { title: "Ohio", value: "USA-OH" },
+ { title: "Oklahoma", value: "USA-OK" },
+ { title: "Oregon", value: "USA-OR" },
+ { title: "Pennsylvania", value: "USA-PA" },
+ { title: "Rhode Island", value: "USA-RI" },
+ { title: "South Carolina", value: "USA-SC" },
+ { title: "South Dakota", value: "USA-SD" },
+ { title: "Tennessee", value: "USA-TN" },
+ { title: "Texas", value: "USA-TX" },
+ { title: "Utah", value: "USA-UT" },
+ { title: "Vermont", value: "USA-VT" },
+ { title: "Virginia", value: "USA-VA" },
+ { title: "Washington", value: "USA-WA" },
+ { title: "West Virginia", value: "USA-WV" },
+ { title: "Wisconsin", value: "USA-WI" },
+ { title: "Wyoming", value: "USA-WY" },
+ { title: "District of Columbia", value: "USA-DC" },
+ { title: "Ontario", value: "CAN-ON" },
+ { title: "Quebec", value: "CAN-QC" },
+ { title: "British Columbia", value: "CAN-BC" },
+ { title: "Alberta", value: "CAN-AB" }
+ ]
+ },
+ {
+ type: "Input.Text",
+ id: "postalCode",
+ label: "Postal/ZIP code",
+ placeholder: "Postal code",
+ isRequired: true,
+ errorMessage: "Postal code is required.",
+ value: If(IsBlank(Topic.currentPostalCode), "", Topic.currentPostalCode)
+ },
+ {
+ type: "Input.Toggle",
+ id: "isPrimary",
+ title: "Set as primary address",
+ value: If(Topic.currentIsPrimary = true, "true", "false"),
+ valueOn: "true",
+ valueOff: "false"
+ }
+ ],
+ actions: [
+ {
+ type: "Action.Submit",
+ title: "Update Address",
+ data: {
+ action: "updateAddress"
+ }
+ },
+ {
+ type: "Action.Submit",
+ title: "Cancel",
+ data: {
+ action: "cancel"
+ }
+ }
+ ]
+ }
+ output:
+ binding:
+ action: Topic.cardAction
+ addressLine1: Topic.newAddressLine1
+ addressLine2: Topic.newAddressLine2
+ city: Topic.newCity
+ country: Topic.newCountry
+ isPrimary: Topic.newIsPrimary
+ postalCode: Topic.newPostalCode
+ stateProvince: Topic.newStateProvince
+
+ outputType:
+ properties:
+ action: String
+ addressLine1: String
+ addressLine2: String
+ city: String
+ country: String
+ isPrimary: String
+ postalCode: String
+ stateProvince: String
+
+ - kind: ConditionGroup
+ id: checkCancelAction
+ conditions:
+ - id: cancelCondition
+ condition: =Topic.cardAction = "cancel"
+ actions:
+ - kind: SendActivity
+ id: sendCancelMessage
+ activity: Address update cancelled. No changes were made.
+
+ - kind: EndDialog
+ id: endOnCancel
+
+ - kind: ConditionGroup
+ id: validateFields
+ conditions:
+ - id: missingFieldsCondition
+ condition: =IsBlank(Topic.newAddressLine1) || IsBlank(Topic.newCity) || IsBlank(Topic.newStateProvince) || IsBlank(Topic.newPostalCode)
+ actions:
+ - kind: SendActivity
+ id: sendValidationError
+ activity: Please fill in all required fields (Address Line 1, City, State/Province, and Postal Code).
+
+ - kind: EndDialog
+ id: endOnValidationError
+
+ - kind: ConditionGroup
+ id: checkAddOrUpdate
+ conditions:
+ - id: isNewAddressCondition
+ condition: =Topic.isNewAddress = true
+ displayName: Add new address
+ actions:
+ - kind: BeginDialog
+ id: addAddress_BeginDialog
+ displayName: Add New Residential Address
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{Event_Effective_Date}"",""value"":"""& Text(Now(), "yyyy-MM-dd") &"""},{""key"":""{Country_Code}"",""value"":""" & Topic.newCountry & """},{""key"":""{State_Province_Code}"",""value"":""" & Topic.newStateProvince & """},{""key"":""{Address_Line_1}"",""value"":""" & Topic.newAddressLine1 & """},{""key"":""{Address_Line_2}"",""value"":""" & If(IsBlank(Topic.newAddressLine2), "", Topic.newAddressLine2) & """},{""key"":""{City}"",""value"":""" & Topic.newCity & """},{""key"":""{Postal_Code}"",""value"":""" & Topic.newPostalCode & """},{""key"":""{Is_Primary}"",""value"":""" & If(Topic.newIsPrimary = "true", "true", "false") & """}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeAddResidentialAddress
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.updateErrorResponse
+ isSuccess: Topic.updateIsSuccess
+ workdayResponse: Topic.updateWorkdayResponse
+
+ elseActions:
+ - kind: BeginDialog
+ id: updateAddress_BeginDialog
+ displayName: Update Residential Address
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{Event_Effective_Date}"",""value"":"""& Text(Now(), "yyyy-MM-dd") &"""},{""key"":""{Address_ID}"",""value"":""" & Topic.selectedAddressID & """},{""key"":""{Country_Code}"",""value"":""" & Topic.newCountry & """},{""key"":""{State_Province_Code}"",""value"":""" & Topic.newStateProvince & """},{""key"":""{Address_Line_1}"",""value"":""" & Topic.newAddressLine1 & """},{""key"":""{Address_Line_2}"",""value"":""" & If(IsBlank(Topic.newAddressLine2), "", Topic.newAddressLine2) & """},{""key"":""{City}"",""value"":""" & Topic.newCity & """},{""key"":""{Postal_Code}"",""value"":""" & Topic.newPostalCode & """},{""key"":""{Is_Primary}"",""value"":""" & If(Topic.newIsPrimary = "true", "true", "false") & """}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeUpdateResidentialAddress
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.updateErrorResponse
+ isSuccess: Topic.updateIsSuccess
+ workdayResponse: Topic.updateWorkdayResponse
+
+ - kind: ConditionGroup
+ id: checkUpdateSuccess
+ conditions:
+ - id: updateSuccessCondition
+ condition: =Topic.updateIsSuccess = true
+ actions:
+ - kind: SendActivity
+ id: sendSuccessIntroMessage
+ activity: Great! You've updated your address.
+
+ - kind: SendActivity
+ id: sendSuccessMessage
+ activity:
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "✅ Address updated",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "TextBlock",
+ text: Topic.newAddressLine1 & ", " & Topic.newCity & ", " & Topic.newStateProvince & " " & Topic.newPostalCode & " (" & If(Topic.newIsPrimary = "true", "Primary", "Home") & ")",
+ wrap: true,
+ spacing: "Small"
+ }
+ ]
+ }
+
+ - kind: CancelAllDialogs
+ id: endOnSuccess
+
+ elseActions:
+ - kind: SendActivity
+ id: sendUpdateErrorMessage
+ activity: |-
+ There was an error updating your address. Please try again later or contact support.
+
+ **Error details:** {Topic.updateErrorResponse}
+
+ - kind: CancelAllDialogs
+ id: endOnUpdateError
+
+inputType: {}
+outputType:
+ properties:
+ updateIsSuccess:
+ displayName: Update Success
+ type: Boolean
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/update_address.png b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/update_address.png
new file mode 100644
index 00000000..690daa35
Binary files /dev/null and b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/EmployeeUpdateResidentialAddress/update_address.png differ
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/README.md b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/README.md
new file mode 100644
index 00000000..4f751033
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/README.md
@@ -0,0 +1,16 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Employee Scenarios
+
+Copilot Studio topics for employee self-service actions in Workday.
+
+| Folder | Description |
+| --- | --- |
+| [EmployeeAddDependents/](./EmployeeAddDependents/) | Add dependents to benefits |
+| [EmployeeGetVacationBalance/](./EmployeeGetVacationBalance/) | Check vacation balance |
+| [EmployeeUpdateResidentialAddress/](./EmployeeUpdateResidentialAddress/) | Update residential address |
+| [WorkdayEmployeeRequestTimeOff/](./WorkdayEmployeeRequestTimeOff/) | Request time off |
+| [WorkdayGetUserProfile/](./WorkdayGetUserProfile/) | View user profile |
+| [WorkdayManageEmergencyContact/](./WorkdayManageEmergencyContact/) | Manage emergency contacts |
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/README.md b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/README.md
new file mode 100644
index 00000000..c0fec5fa
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/README.md
@@ -0,0 +1,229 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Employee Request Time Off
+
+This topic lets employees submit single-day or multi-day time off requests to Workday directly from a Copilot Studio agent.
+
+When an employee triggers this topic, the agent:
+
+1. Fetches current leave balances from Workday
+2. Shows an Adaptive Card form with leave type, dates, hours, and reason
+3. Submits the request to the Workday `Enter_Time_Off` API
+4. Displays a confirmation card or a friendly error with retry options
+
+**Example trigger phrases:** "Request time off" · "Request vacation from January 5th to January 10th" · "Submit sick leave for next week"
+
+
+
+
+{: .important }
+> This topic ships with sample values from a reference Workday tenant. Every Workday tenant is configured differently — you must replace the Plan IDs, Reason IDs, leave types, and tenant URL with values from your own Workday setup before importing. See [Configure the topic](#configure-the-topic) for details.
+
+## Prerequisites
+
+Before you start, make sure you have:
+
+- The **msdyn_copilotforemployeeselfservicehr** managed solution installed in your agent
+- A Workday tenant with the **Absence_Management** module enabled
+- A Workday connector configured with **User**-level authentication in your Power Platform environment
+- The global variable **`Global.ESS_UserContext_Employee_Id`** populated at session start with the logged-in employee's Workday Employee ID
+- The **`msdyn_HRWorkdayHCMEmployeeGetVacationBalance`** template already imported (from the [EmployeeGetVacationBalance](../EmployeeGetVacationBalance/) folder)
+- Your organization's Workday Absence Plan IDs, Time Off Type IDs, and Reason IDs
+
+## What's in this folder
+
+| File | Description | Do you need to edit it? |
+|------|-------------|------------------------|
+| `topic.yaml` | Conversation flow, Adaptive Card form, configuration tables, error handling | Yes — update tenant URL, Plan IDs, Reason IDs |
+| `msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay.xml` | SOAP template for the Workday `Enter_Time_Off` API | Usually no |
+| `requestTimeOffAdapativeCard.png` | Screenshot of the Adaptive Card form | No |
+| `requestTimeOffSubmitted.png` | Screenshot of the confirmation card | No |
+| `README.md` | This file | No |
+
+## Import the templates
+
+Before configuring the topic, import the XML templates into your agent.
+
+1. Add `msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay.xml` to your agent's ESS Template Configuration.
+
+2. If you haven't already, import the balance template `msdyn_HRWorkdayHCMEmployeeGetVacationBalance` from the [EmployeeGetVacationBalance](../EmployeeGetVacationBalance/) folder. This topic calls it to display leave balances.
+
+## Configure the topic
+
+Open `topic.yaml` and update the following values to match your Workday tenant. All sample values shown below come from a reference tenant and **must** be replaced.
+
+### Step 1: Set your Workday tenant URL
+
+Find the `set_workday_url` node and replace `` with your tenant name:
+
+```yaml
+# Before
+value: https://impl.workday.com//home.htmld
+
+# After — replace contoso_corp with your tenant
+value: https://impl.workday.com/contoso_corp/home.htmld
+```
+
+Your Workday URL typically follows the pattern `https://.workday.com//home.htmld`. Check your browser address bar when logged into Workday.
+
+### Step 2: Update Plan IDs
+
+Find the `set_plan_config` node. Replace each `PlanID` with the corresponding ID from your Workday tenant:
+
+```
+=Table(
+ {PlanID: "ABSENCE_PLAN-6-139", DisplayName: "Floating holiday"},
+ {PlanID: "ABSENCE_PLAN-6-159", DisplayName: "Sick leave"},
+ {PlanID: "ABSENCE_PLAN-6-158", DisplayName: "Vacation"}
+)
+```
+
+You can find your Plan IDs in Workday under **Setup > Absence Plans > Plan ID**. Only plans listed here **and** returned by the Workday API will appear in the balance header.
+
+### Step 3: Update Reason IDs
+
+Find the **second** `set_time_off_reason_config` node. Replace each `ReasonID` with your tenant's value:
+
+| ReasonKey | TimeOffTypeID | ReasonID ← replace this |
+|-----------|---------------|------------------------|
+| `full_day` | `ESS_Vacation_Hours` | `Full Day_Vac` |
+| `half_day_am` | `ESS_Vacation_Hours` | `Half Day_AM_Vac` |
+| `half_day_pm` | `ESS_Vacation_Hours` | `Half Day_PM_Vac` |
+| `full_day` | `ESS_Floating_Holiday_Hours` | `full_day_floating` |
+| `half_day_am` | `ESS_Floating_Holiday_Hours` | `half_day_am_floating` |
+| `half_day_pm` | `ESS_Floating_Holiday_Hours` | `half_day_pm_floating` |
+| `full_day` | `ESS_Comp_Off` | `Full day-COI` |
+| `half_day_am` | `ESS_Comp_Off` | `Half day AM-COI` |
+| `half_day_pm` | `ESS_Comp_Off` | `Half day PM-COI` |
+| `full_day` | `ESS_Sick_Time_Off` | `Full day_Sick_IN` |
+| `half_day_am` | `ESS_Sick_Time_Off` | `Half day AM_Sick_IN` |
+| `half_day_pm` | `ESS_Sick_Time_Off` | `Half day PM_Sick_IN` |
+
+Don't rename the `ReasonKey` values — they're bound to the Adaptive Card "Reason for time off" dropdown.
+
+### Step 4 (optional): Update leave-type choices
+
+If your organization uses different leave types than Vacation, Floating holiday, Sick leave, and Comp off, update two places:
+
+**The Adaptive Card dropdown** — in the `collect_time_off` node, edit the `choices` array. For example, to add Bereavement:
+
+```json
+{ "title": "Bereavement", "value": "ESS_Bereavement" }
+```
+
+**The keyword mapping** — in the `set_leave_type_config` node, add a row so the agent can pre-select the type from the user's utterance:
+
+| Keywords (comma-separated) | LeaveTypeValue |
+|---------------------------|----------------|
+| `vacation,annual,pto,holiday pay` | `Vacation_Hours` |
+| `floating,floater,float day` | `Floating_Holiday_Hours` |
+| `sick,illness,medical,unwell,not feeling well` | `ESS_Sick_Time_Off` |
+
+If you add a new leave type, also add matching rows to `TimeOffReasonConfig` (one per ReasonKey).
+
+### Step 5 (optional): Update the Workday icon
+
+Find the `set_workday_icon_url` node and replace the 1×1 pixel placeholder with your organization's Workday icon URL.
+
+## Import and test the topic
+
+1. In Copilot Studio, go to **Topics > + Add a topic > From file**.
+2. Select `topic.yaml` and import.
+3. Open **Test your agent** and try these:
+
+| What to test | What to expect |
+|-------------|---------------|
+| Say "I want to request time off" | Balance header appears, then the Adaptive Card form |
+| Say "Request vacation from September 15 to September 19" and submit | Success card with date range, type, hours, and reason |
+| Submit with end date before start date | Error: "The end date can't be earlier than the start date" with retry |
+| Select "I don't see my leave type listed" and submit | Redirect message with a link to Workday |
+| Click **Cancel** | "Leave request was cancelled - nothing was submitted" |
+| Submit dates that overlap an existing request | Friendly AI-rewritten error with retry |
+
+If balances show as empty but the form appears, your `PlanConfig` Plan IDs likely don't match your Workday tenant.
+
+## XML template reference
+
+The `msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay.xml` file is a SOAP template for the Workday `Enter_Time_Off_Request` operation. You usually don't need to edit it — placeholder tokens like `{Employee_ID}` and `{timeoff_Dates}` are filled at runtime.
+
+Two settings you might want to change:
+
+| Setting | Default | Change if… |
+|---------|---------|------------|
+| `` | `false` (requires manager approval) | Your process skips approval — set to `true` |
+| `` and `wd:version` | `v42.0` | Your tenant uses a different API version |
+
+{: .important }
+> If you change the API version, update it in **both** the `` element and the `wd:version` attribute. Mismatched versions cause API errors.
+
+## What you can safely edit in the YAML
+
+| What | Where in `topic.yaml` |
+|------|----------------------|
+| Workday tenant URL | `set_workday_url` node |
+| Plan IDs | `set_plan_config` node |
+| Reason IDs | Second `set_time_off_reason_config` node |
+| Leave-type keywords | `set_leave_type_config` node |
+| Adaptive Card dropdown choices | `collect_time_off` node → `choices` array |
+| Trigger phrases | `modelDescription` block |
+| Workday icon | `set_workday_icon_url` node |
+| Balance effective date | `set_as_of_effective_date` node (defaults to `Today()`) |
+
+**Don't** change these — they'll break the topic:
+
+- Node `id` values (`id: main`, `id: collect_time_off`, etc.) — referenced by `GotoAction` jumps
+- `kind:` values — Copilot Studio node types
+- `dialog:` references — point to the shared solution
+- `output: binding:` mappings — must match shared dialog outputs
+- XML placeholder tokens (`{Employee_ID}`, `{timeoff_Dates}`, etc.) — filled at runtime
+- The `EmployeeName` input — the model uses it to validate the request is for the logged-in user
+
+## Dependencies
+
+This topic depends on three external components:
+
+- **Workday `Get_Time_Off_Plan_Balances`** — Absence_Management service, User-level auth. Uses the `msdyn_HRWorkdayHCMEmployeeGetVacationBalance` template from [EmployeeGetVacationBalance](../EmployeeGetVacationBalance/). Called at topic start to show balances.
+
+- **Workday `Enter_Time_Off`** — Absence_Management v42.0, User-level auth. Uses the `msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay` template in this folder. Called after form submission.
+
+- **`WorkdaySystemGetCommonExecution` dialog** — from the `msdyn_copilotforemployeeselfservicehr` solution. Executes XML templates against the Workday connector. Must be pre-installed.
+
+## Troubleshooting
+
+**Balance header is empty or shows all zeros**
+Your `PlanConfig` Plan IDs don't match your Workday tenant, or the balance template isn't imported. Verify the IDs and confirm `msdyn_HRWorkdayHCMEmployeeGetVacationBalance` is in your ESS Template Configuration.
+
+**"Something went wrong" when submitting**
+The `ReasonID` values in `TimeOffReasonConfig` don't match your Workday tenant. Check your IDs in Workday under **Setup > Time Off Reasons**.
+
+**Authentication error**
+`Global.ESS_UserContext_Employee_Id` is empty or the Workday connector isn't configured. Verify both.
+
+**Vacation isn't pre-selected when the user says "request vacation"**
+The keyword mapping uses `Vacation_Hours` but the dropdown value is `ESS_Vacation_Hours`. Update `LeaveTypeConfig` to use `ESS_Vacation_Hours`. See [Open Questions](#open-questions).
+
+**Success card shows a raw ID like `ESS_Vacation_Hours`**
+The `Switch()` expression in the success card doesn't match the dropdown values. See [Open Questions](#open-questions).
+
+## Tips
+
+- Import the balance template **before** this topic — the balance lookup runs at topic start and fails silently if the template is missing.
+- Update `PlanConfig`, `TimeOffReasonConfig`, **and** the Adaptive Card `choices` together — they're interconnected but maintained separately.
+- Test with a real Workday employee ID that has leave balances.
+- Keep `ReasonKey` values as `full_day`, `half_day_am`, `half_day_pm` — they're bound to the Adaptive Card dropdown.
+
+## Handle repo updates
+
+When this repo publishes a new version of the topic:
+
+1. **Diff first.** Your customized tenant URL, Plan IDs, Reason IDs, and leave-type keywords will be overwritten if you re-import `topic.yaml`.
+2. **Merge.** Copy your config values from the current topic, pull the updated file, paste your values back, then import.
+3. The XML template is safe to overwrite — unless you changed `Auto_Complete` or the API version.
+
+## Known limitations
+
+- **Leave type pre-selection may not work for Vacation and Floating Holiday.** The keyword mapping (`LeaveTypeConfig`) uses `Vacation_Hours` and `Floating_Holiday_Hours`, but the Adaptive Card dropdown expects `ESS_Vacation_Hours` and `ESS_Floating_Holiday_Hours`. To fix this, update the `LeaveTypeValue` entries in `LeaveTypeConfig` to include the `ESS_` prefix.
+
+- **Success card may show raw IDs instead of friendly names.** After a successful submission, the confirmation card may display `ESS_Vacation_Hours` instead of "Vacation". To work around this, update the `Switch()` expression in the success card to match the `ESS_`-prefixed values from the dropdown.
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay.xml
new file mode 100644
index 00000000..9d4a5342
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay.xml
@@ -0,0 +1,61 @@
+
+
+
+
+ User
+
+ msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay
+ Absence_Management
+ v42.0
+
+
+
+
+ //*[local-name()='Time_Off_Event_Reference']/*[local-name()='ID' and @*[local-name()='type']='WID']
+ Event_WID
+
+
+ //*[local-name()='Time_Off_Entry_Reference']
+ Entry_References
+
+
+
+
+
+
+
+
+
+ false
+ true
+ true
+
+ {timeoff_Comment}
+
+
+
+
+ {Employee_ID}
+
+
+
+
+ {timeoff_Date}
+ {timeoff_Hours_Per_Day}
+
+ {timeoff_Time_Off_Type}
+
+
+
+
+
+ {timeoff_Reason}
+
+ {timeoff_Comment}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/requestTimeOffAdapativeCard.png b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/requestTimeOffAdapativeCard.png
new file mode 100644
index 00000000..649b781b
Binary files /dev/null and b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/requestTimeOffAdapativeCard.png differ
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/requestTimeOffSubmitted.png b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/requestTimeOffSubmitted.png
new file mode 100644
index 00000000..5b41ff1e
Binary files /dev/null and b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/requestTimeOffSubmitted.png differ
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/request_time_off.png b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/request_time_off.png
new file mode 100644
index 00000000..e2577532
Binary files /dev/null and b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/request_time_off.png differ
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/topic.yaml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/topic.yaml
new file mode 100644
index 00000000..03d6380f
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeeRequestTimeOff/topic.yaml
@@ -0,0 +1,910 @@
+kind: AdaptiveDialog
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: EmployeeName
+ description: The name of the employee to fetch data for.
+ entity: PersonNamePrebuiltEntity
+ shouldPromptUser: false
+
+ - kind: AutomaticTaskInput
+ propertyName: InputStartDate
+ description: The start date of the time off request.
+ entity: DateTimePrebuiltEntity
+ shouldPromptUser: false
+
+ - kind: AutomaticTaskInput
+ propertyName: InputEndDate
+ description: The end date of the time off request.
+ entity: DateTimePrebuiltEntity
+ shouldPromptUser: false
+
+ - kind: AutomaticTaskInput
+ propertyName: InputHoursPerDay
+ description: The number of hours per day for the time off request.
+ entity: NumberPrebuiltEntity
+ shouldPromptUser: false
+
+ - kind: AutomaticTaskInput
+ propertyName: InputTimeOffType
+ description: Extract the type of leave mentioned such as vacation, sick, sick leave, floating holiday, floater, PTO, annual leave, medical, or illness. Extract keywords like 'sick', 'vacation', 'floating', 'PTO', 'annual'.
+ entity: StringPrebuiltEntity
+ shouldPromptUser: false
+
+ - kind: AutomaticTaskInput
+ propertyName: InputLeaveReason
+ description: The reason or comment for the time off request. Extract any reason, justification, or explanation the user provides for their leave such as 'family event', 'doctor appointment', 'personal', 'travel', etc.
+ entity: StringPrebuiltEntity
+ shouldPromptUser: false
+
+modelDescription: |-
+ Use this topic when the user wants to REQUEST, SUBMIT, APPLY FOR, BOOK, or TAKE THEIR OWN time off / leave in Workday (Absence_Management) — vacation, PTO, paid time off, holiday, sick leave, personal day, parental leave, bereavement leave, jury duty, sabbatical, or any absence type.
+
+ Trigger phrases:
+ - "Request time off"
+ - "I need to submit time off"
+ - "Please request vacation from to "
+ - "Request sick leave for next week"
+ - "I need time off from to for "
+ - "Apply for / book / take a vacation / leave"
+
+ If submitting for themselves, the topic may prompt for: time off type, start date, end date, reason, and hours.
+
+ Data belongs exclusively to the requesting user.
+
+ Do NOT trigger for:
+ - Requesting time off for someone else
+ - Viewing existing time-off balances or history (different topic)
+ - Cancelling/modifying a submitted request (different topic)
+ - General questions about leave policy or accruals
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent: {}
+ actions:
+ - kind: SetVariable
+ id: set_workday_url
+ variable: Topic.WorkdayUrl
+ value: https://impl.workday.com//home.htmld
+
+ - kind: SetVariable
+ id: set_workday_icon_url
+ variable: Topic.WorkdayIconUrl
+ value: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=
+
+ # ================================================================
+ # DATE CONFIGURATION
+ # Set the effective date for balance lookup. Default is Today().
+ # Customers can change this to a specific date if needed.
+ # ================================================================
+ - kind: SetVariable
+ id: set_as_of_effective_date
+ variable: Topic.AsOfEffectiveDate
+ value: =Today()
+
+ # ================================================================
+ # REASON MAPPING CONFIGURATION
+ # Maps generic reason (Full Day / Half Day AM / Half Day PM) +
+ # Time Off Type to the correct Workday ReasonID.
+ # ReasonKey must match the dropdown values below.
+ # TimeOffTypeID must match the time off type dropdown values.
+ # ================================================================
+ - kind: SetVariable
+ id: set_time_off_reason_config
+ variable: Topic.TimeOffReasonConfig
+ value: |-
+ =Table(
+ {ReasonKey: "full_day", TimeOffTypeID: "ESS_Floating_Holiday_Hours", ReasonID: "full_day_floating"},
+ {ReasonKey: "half_day_am", TimeOffTypeID: "ESS_Floating_Holiday_Hours", ReasonID: "half_day_am_floating"},
+ {ReasonKey: "half_day_pm", TimeOffTypeID: "ESS_Floating_Holiday_Hours", ReasonID: "half_day_pm_floating"},
+ {ReasonKey: "full_day", TimeOffTypeID: "ESS_Comp_Off", ReasonID: "Full day-COI"},
+ {ReasonKey: "half_day_am", TimeOffTypeID: "ESS_Comp_Off", ReasonID: "Half day AM-COI"},
+ {ReasonKey: "half_day_pm", TimeOffTypeID: "ESS_Comp_Off", ReasonID: "Half day PM-COI"},
+ {ReasonKey: "full_day", TimeOffTypeID: "ESS_Sick_Time_Off", ReasonID: "Full day_Sick_IN"},
+ {ReasonKey: "half_day_am", TimeOffTypeID: "ESS_Sick_Time_Off", ReasonID: "Half day AM_Sick_IN"},
+ {ReasonKey: "half_day_pm", TimeOffTypeID: "ESS_Sick_Time_Off", ReasonID: "Half day PM_Sick_IN"},
+ {ReasonKey: "full_day", TimeOffTypeID: "ESS_Vacation_Hours", ReasonID: "Full Day_Vac"},
+ {ReasonKey: "half_day_am", TimeOffTypeID: "ESS_Vacation_Hours", ReasonID: "Half Day_AM_Vac"},
+ {ReasonKey: "half_day_pm", TimeOffTypeID: "ESS_Vacation_Hours", ReasonID: "Half Day_PM_Vac"}
+ )
+
+ # ================================================================
+ # LEAVE TYPE CONFIGURATION
+ # Edit keywords below to customize how user phrases map to leave types.
+ # Keywords are comma-separated and case-insensitive.
+ # LeaveTypeValue must match the dropdown choice values.
+ # ================================================================
+ - kind: SetVariable
+ id: set_leave_type_config
+ variable: Topic.LeaveTypeConfig
+ value: |-
+ =Table(
+ {Keywords: "vacation,annual,pto,holiday pay", LeaveTypeValue: "ESS_Vacation_Hours"},
+ {Keywords: "floating,floater,float day", LeaveTypeValue: "ESS_Floating_Holiday_Hours"},
+ {Keywords: "sick,illness,medical,unwell,not feeling well", LeaveTypeValue: "ESS_Sick_Time_Off"}
+ )
+
+ # Map extracted time off type to dropdown value using config table
+ - kind: SetVariable
+ id: set_mapped_time_off_type
+ variable: Topic.MappedTimeOffType
+ value: |-
+ =If(
+ IsBlank(Topic.InputTimeOffType),
+ "",
+ Coalesce(
+ First(
+ Filter(
+ Topic.LeaveTypeConfig,
+ !IsEmpty(Filter(Split(Keywords, ","), Trim(Value) in Lower(Topic.InputTimeOffType)))
+ )
+ ).LeaveTypeValue,
+ ""
+ )
+ )
+
+ - kind: BeginDialog
+ id: get_leave_balance
+ displayName: Get Leave Balance
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":""" & Text(Topic.AsOfEffectiveDate, "yyyy-MM-dd") & """}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetVacationBalance
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.balanceErrorResponse
+ isSuccess: Topic.balanceIsSuccess
+ workdayResponse: Topic.balanceResponse
+
+ - kind: ParseValue
+ id: parse_balance
+ variable: Topic.parsedBalance
+ valueType:
+ kind: Record
+ properties:
+ PlanDescriptor:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ PlanID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ RemainingBalance:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ UnitOfTime:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.balanceResponse
+
+ # ================================================================
+ # PLAN BALANCE CONFIGURATION
+ # Maps Plan IDs (from balance API) to display names.
+ # Update PlanID values to match your Workday configuration.
+ # Only plans listed here AND returned by the API will be displayed.
+ # ================================================================
+ - kind: SetVariable
+ id: set_plan_config
+ variable: Topic.PlanConfig
+ value: |-
+ =Table(
+ {PlanID: "ABSENCE_PLAN-6-139", DisplayName: "Floating holiday"},
+ {PlanID: "ABSENCE_PLAN-6-159", DisplayName: "Sick leave"},
+ {PlanID: "ABSENCE_PLAN-6-158", DisplayName: "Vacation"}
+ )
+
+ # Create merged balance table for easy lookup by PlanID
+ - kind: SetVariable
+ id: set_balance_table
+ variable: Topic.BalanceTable
+ value: |-
+ =ForAll(
+ Sequence(CountRows(Topic.parsedBalance.PlanID)),
+ {
+ PlanID: Index(Topic.parsedBalance.PlanID, Value).Value,
+ Balance: Index(Topic.parsedBalance.RemainingBalance, Value).Value
+ }
+ )
+
+ # Build display data: only include plans that exist in BOTH config AND API response
+ - kind: SetVariable
+ id: set_display_balances
+ variable: Topic.DisplayBalances
+ value: |-
+ =ForAll(
+ Filter(
+ Topic.PlanConfig,
+ !IsBlank(LookUp(Topic.BalanceTable, PlanID = ThisRecord.PlanID).Balance)
+ ) As plan,
+ {
+ PlanID: plan.PlanID,
+ DisplayName: plan.DisplayName,
+ Balance: LookUp(Topic.BalanceTable, PlanID = plan.PlanID).Balance
+ }
+ )
+
+ - kind: SetVariable
+ id: build_intro_message
+ variable: Topic.introMessage
+ value: ="Sure, I'll help you submit a time off request. Here's a form where you can choose the type of leave and dates you want off. Don't see the type of leave you want? [Book directly in Workday](" & Topic.WorkdayUrl & ")"
+
+ - kind: SendActivity
+ id: intro_message
+ activity: "{Topic.introMessage}"
+
+ - kind: ConditionGroup
+ id: need_inputs
+ conditions:
+ - id: always_true
+ condition: true
+ actions:
+ - kind: AdaptiveCardPrompt
+ id: collect_time_off
+ displayName: Ask time off type, start date, end date, reason and hours
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Book your time off",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ color: "Default"
+ },
+ {
+ type: "Container",
+ style: "emphasis",
+ bleed: true,
+ items: [
+ {
+ type: "TextBlock",
+ text: "Available balance in hours as of " & Text(Topic.AsOfEffectiveDate, "mmmm d, yyyy"),
+ size: "Small",
+ weight: "Bolder",
+ wrap: true
+ },
+ {
+ type: "ColumnSet",
+ columns: ForAll(
+ Topic.DisplayBalances,
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ { type: "TextBlock", text: DisplayName, size: "Small", wrap: true },
+ { type: "TextBlock", text: Text(Balance), weight: "Bolder", spacing: "Small" }
+ ]
+ }
+ )
+ }
+ ]
+ },
+ {
+ type: "TextBlock",
+ text: "Fields marked with * are required.",
+ wrap: true,
+ spacing: "Medium",
+ size: "Small"
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "timeOffType",
+ label: "Type of time off",
+ style: "compact",
+ value: Topic.MappedTimeOffType,
+ isRequired: true,
+ errorMessage: "Please select a type.",
+ choices: [
+ { title: "Vacation", value: "ESS_Vacation_Hours" },
+ { title: "Floating holiday", value: "ESS_Floating_Holiday_Hours" },
+ { title: "Sick leave", value: "ESS_Sick_Time_Off" },
+ { title: "Comp off", value: "ESS_Comp_Off" },
+ { title: "I don't see my leave type listed", value: "NOT_LISTED" }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Date",
+ id: "startDate",
+ label: "Start date",
+ value: If(IsBlank(Topic.InputStartDate), "", Text(Topic.InputStartDate, "yyyy-MM-dd")),
+ isRequired: true,
+ errorMessage: "Please select a start date."
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Date",
+ id: "endDate",
+ label: "End date",
+ value: If(IsBlank(Topic.InputEndDate), "", Text(Topic.InputEndDate, "yyyy-MM-dd")),
+ isRequired: true,
+ errorMessage: "Please select an end date."
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "Input.Number",
+ id: "hoursPerDay",
+ label: "Hours per day",
+ min: 1,
+ max: 24,
+ value: If(IsBlank(Topic.InputHoursPerDay), 8, Value(Topic.InputHoursPerDay)),
+ isRequired: true,
+ errorMessage: "Please enter hours per day."
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "timeOffReason",
+ label: "Reason for time off",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a reason.",
+ choices: [
+ { title: "Full Day", value: "full_day" },
+ { title: "Half Day AM", value: "half_day_am" },
+ { title: "Half Day PM", value: "half_day_pm" }
+ ]
+ },
+ {
+ type: "Input.Text",
+ id: "leaveComment",
+ label: "Additional comments (optional)",
+ placeholder: "e.g. Details about your time off",
+ value: If(IsBlank(Topic.InputLeaveReason), "", Topic.InputLeaveReason),
+ isMultiline: true,
+ isRequired: false
+ },
+ {
+ type: "ColumnSet",
+ spacing: "Medium",
+ columns: [
+ {
+ type: "Column",
+ width: "auto",
+ items: [
+ {
+ type: "ActionSet",
+ actions: [
+ { type: "Action.Submit", title: "Submit", id: "Submit", data: { actionSubmitId: "Submit" } },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", data: { actionSubmitId: "Cancel" }, associatedInputs: "none" }
+ ]
+ }
+ ],
+ verticalContentAlignment: "Center"
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [],
+ verticalContentAlignment: "Center"
+ },
+ {
+ type: "Column",
+ width: "auto",
+ items: [
+ {
+ type: "ColumnSet",
+ spacing: "None",
+ columns: [
+ {
+ type: "Column",
+ width: "auto",
+ items: [
+ {
+ type: "Image",
+ url: Topic.WorkdayIconUrl,
+ style: "RoundedCorners",
+ size: "Small",
+ height: "20px",
+ width: "20px"
+ }
+ ],
+ verticalContentAlignment: "Center"
+ },
+ {
+ type: "Column",
+ width: "auto",
+ items: [
+ {
+ type: "TextBlock",
+ text: "Workday",
+ size: "Small",
+ color: "#242424",
+ weight: "Bolder"
+ }
+ ],
+ verticalContentAlignment: "Center",
+ spacing: "Small"
+ }
+ ]
+ }
+ ],
+ verticalContentAlignment: "Center"
+ }
+ ]
+ }
+ ],
+ actions: []
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.actionSubmitId
+ endDate: Topic.endDate
+ hoursPerDay: Topic.hoursPerDay
+ leaveComment: Topic.leaveComment
+ startDate: Topic.startDate
+ timeOffReason: Topic.timeOffReason
+ timeOffType: Topic.timeOffType
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ endDate: String
+ hoursPerDay: String
+ leaveComment: String
+ startDate: String
+ timeOffReason: String
+ timeOffType: String
+
+ - kind: ConditionGroup
+ id: handle_cancel
+ conditions:
+ - id: cancel_pressed
+ condition: =Topic.actionSubmitId = "Cancel"
+ actions:
+ - kind: SendActivity
+ id: cancel_processing_msg
+ activity: Processing cancellation...
+
+ - kind: SendActivity
+ id: cancel_ack_msg
+ activity: Leave request was cancelled - nothing was submitted.
+
+ - kind: SendActivity
+ id: cancel_followup_msg
+ activity: No worries, the form has been discarded and nothing was submitted to Workday. Want to start a new request or need anything else?
+
+ - kind: EndDialog
+ id: end_on_cancel
+
+ # Handle "I don't see my leave type listed" selection
+ - kind: ConditionGroup
+ id: handle_not_listed
+ conditions:
+ - id: type_not_listed
+ condition: =Topic.timeOffType = "NOT_LISTED"
+ actions:
+ - kind: SetVariable
+ id: set_not_listed_message
+ variable: Topic.notListedMessage
+ value: ="Since your leave type isn't listed here, you can book directly in [Workday](" & Topic.WorkdayUrl & "), or let me know if you want to change your type."
+
+ - kind: SendActivity
+ id: not_listed_msg
+ activity: "{Topic.notListedMessage}"
+
+ - kind: CancelAllDialogs
+ id: end_not_listed
+
+ # Validate end date >= start date
+ - kind: ConditionGroup
+ id: validate_dates
+ conditions:
+ - id: end_before_start
+ condition: =!IsBlank(Topic.endDate) && !IsBlank(Topic.startDate) && DateValue(Topic.endDate) < DateValue(Topic.startDate)
+ actions:
+ - kind: SendActivity
+ id: date_error_msg
+ activity: The end date can't be earlier than the start date.
+
+ # Ask user if they want to try again
+ - kind: Question
+ id: ask_retry_dates
+ interruptionPolicy:
+ allowInterruption: false
+
+ variable: Topic.retryDateChoice
+ prompt: Would you like to try with different dates?
+ entity: BooleanPrebuiltEntity
+
+ - kind: ConditionGroup
+ id: handle_retry_date_choice
+ conditions:
+ - id: user_wants_retry_dates
+ condition: =Topic.retryDateChoice = true
+ actions:
+ - kind: GotoAction
+ id: goto_form_on_date_retry
+ actionId: collect_time_off
+
+ elseActions:
+ - kind: SendActivity
+ id: end_on_no_date_retry
+ activity: No problem! Let me know if you need anything else.
+
+ - kind: CancelAllDialogs
+ id: cancel_on_no_date_retry
+
+ # ========================================================
+ # V3: Build list of dates and pass to Plugin
+ # ========================================================
+
+ # Map selected Time Off Type + Reason to Workday ReasonID
+ - kind: SetVariable
+ id: resolve_reason_id
+ variable: Topic.resolvedReasonID
+ value: =LookUp(Topic.TimeOffReasonConfig, ReasonKey = Topic.timeOffReason && TimeOffTypeID = Topic.timeOffType).ReasonID
+
+ - kind: SetVariable
+ id: set_time_off_comment
+ variable: Topic.timeOffComment
+ value: =If(IsBlank(Topic.leaveComment), "ess generated time off request", Topic.leaveComment)
+
+ # Initialize date list (empty string)
+ - kind: SetVariable
+ id: init_date_list
+ variable: Topic.dateList
+ value: =""
+
+ # Initialize loop counter
+ - kind: SetVariable
+ id: init_iterator
+ variable: Topic.newIterator
+ value: =0
+
+ # Set up loop variable
+ - kind: SetVariable
+ id: set_iterator
+ variable: Topic.iterator
+ value: =Topic.newIterator
+
+ # Calculate current date for this iteration
+ - kind: SetVariable
+ id: calc_current_date
+ variable: Topic.currentDate
+ value: =Text(DateAdd(DateValue(Topic.startDate), Topic.iterator, TimeUnit.Days), "yyyy-MM-dd")
+
+ # Loop to build comma-separated date list
+ - kind: ConditionGroup
+ id: build_date_list_loop
+ conditions:
+ - id: should_continue_building
+ condition: =DateValue(Topic.currentDate) <= DateValue(Topic.endDate)
+ actions:
+ # Append date to list (with comma separator if not first)
+ - kind: SetVariable
+ id: append_date
+ variable: Topic.dateList
+ value: =If(Topic.dateList = "", Topic.currentDate, Topic.dateList & "," & Topic.currentDate)
+
+ # Increment counter
+ - kind: SetVariable
+ id: increment_iterator
+ variable: Topic.newIterator
+ value: =Topic.newIterator + 1
+
+ # Continue loop
+ - kind: GotoAction
+ id: goto_build_loop
+ actionId: set_iterator
+
+ # Total days is the count from loop
+ - kind: SetVariable
+ id: calc_total_days
+ variable: Topic.totalDays
+ value: =Topic.newIterator
+
+ # Make single API call - Plugin will build XML entries from date list
+ - kind: BeginDialog
+ id: execute_workday_multiday
+ displayName: Submit Multi-Day Time Off Request
+ input:
+ binding:
+ parameters: |-
+ ="{""params"":[" &
+ "{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """}," &
+ "{""key"":""{timeoff_Dates}"",""value"":""" & Topic.dateList & """}," &
+ "{""key"":""{timeoff_Time_Off_Type}"",""value"":""" & Topic.timeOffType & """}," &
+ "{""key"":""{timeoff_Hours_Per_Day}"",""value"":""" & Topic.hoursPerDay & """}," &
+ "{""key"":""{timeoff_Comment}"",""value"":""" & Topic.timeOffComment & """}," &
+ "{""key"":""{timeoff_Reason}"",""value"":""" & Topic.resolvedReasonID & """}" &
+ "]}"
+ scenarioName: msdyn_HRWorkdayAbsenceEnterTimeOff_MultiDay
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ # Parse error message for display
+ - kind: SetVariable
+ id: init_last_error
+ variable: Topic.lastErrorMessage
+ value: =If(Topic.isSuccess = true, "", Topic.errorResponse)
+
+ - kind: SetVariable
+ id: parse_error_message
+ variable: Topic.friendlyErrorMessage
+ value: =IfError(Text(ParseJSON(Topic.lastErrorMessage).error.message), IfError(Text(ParseJSON(Topic.lastErrorMessage).message), Topic.lastErrorMessage))
+
+ - kind: ConditionGroup
+ id: report_result
+ conditions:
+ - id: request_failed
+ condition: =Topic.isSuccess = false
+ actions:
+ # Use AI to generate a friendly, conversational error message
+ - kind: AnswerQuestionWithAI
+ id: generate_friendly_error
+ autoSend: false
+ variable: Topic.aiGeneratedError
+ userInput: =Topic.friendlyErrorMessage
+ additionalInstructions: Reframe the following Workday error message in a friendly way for an employee. Keep it to ONE short sentence describing what went wrong. Do NOT include suggestions or next steps. Do NOT apologize. Do NOT use technical jargon. Example output format - "The dates you selected conflict with an existing request."
+
+ # Set final error message with fallback
+ - kind: SetVariable
+ id: set_final_error_message
+ variable: Topic.finalErrorMessage
+ value: =If(IsBlank(Topic.aiGeneratedError), "The dates you selected aren't available.", Topic.aiGeneratedError)
+
+ - kind: SendActivity
+ id: friendly_error_message
+ activity: "{Topic.finalErrorMessage}"
+
+ # Ask user if they want to try again
+ - kind: Question
+ id: ask_retry
+ interruptionPolicy:
+ allowInterruption: false
+
+ variable: Topic.retryChoice
+ prompt: Would you like to try with different dates?
+ entity: BooleanPrebuiltEntity
+
+ - kind: ConditionGroup
+ id: handle_retry_choice
+ conditions:
+ - id: user_wants_retry
+ condition: =Topic.retryChoice = true
+ actions:
+ - kind: GotoAction
+ id: goto_form_on_retry
+ actionId: collect_time_off
+
+ elseActions:
+ - kind: SendActivity
+ id: end_on_no_retry
+ activity: No problem! Let me know if you need anything else.
+
+ - kind: CancelAllDialogs
+ id: cancel_on_no_retry
+
+ elseActions:
+ - kind: SendActivity
+ id: success_card
+ activity:
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "✅ Request submitted",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true
+ },
+ {
+ type: "TextBlock",
+ text: "Your time off request has been successfully submitted and is now pending approval.",
+ wrap: true,
+ spacing: "Small"
+ },
+ {
+ type: "Table",
+ spacing: "Medium",
+ showGridLines: false,
+ firstRowAsHeader: false,
+ columns: [
+ { width: 1 },
+ { width: 2 }
+ ],
+ rows: [
+ {
+ type: "TableRow",
+ cells: [
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: "Type of time off", weight: "Bolder" }]
+ },
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: Switch(Topic.timeOffType, "ESS_Vacation_Hours", "Vacation", "ESS_Floating_Holiday_Hours", "Floating holiday", "ESS_Sick_Time_Off", "Sick leave", "ESS_Comp_Off", "Comp off", Topic.timeOffType), wrap: true }]
+ }
+ ]
+ },
+ {
+ type: "TableRow",
+ cells: [
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: "Time off date range", weight: "Bolder" }]
+ },
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: Text(DateValue(Topic.startDate), "mmmm d, yyyy") & " to " & Text(DateValue(Topic.endDate), "mmmm d, yyyy") & " (" & Text(Topic.totalDays) & " days)", wrap: true }]
+ }
+ ]
+ },
+ {
+ type: "TableRow",
+ cells: [
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: "Total hours", weight: "Bolder" }]
+ },
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: Text(Topic.totalDays * Value(Topic.hoursPerDay)) & " hours (" & Text(Topic.totalDays) & " x " & Topic.hoursPerDay & "-hour days)", wrap: true }]
+ }
+ ]
+ },
+ {
+ type: "TableRow",
+ cells: [
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: "Reason", weight: "Bolder" }]
+ },
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: Switch(Topic.timeOffReason, "full_day", "Full Day", "half_day_am", "Half Day AM", "half_day_pm", "Half Day PM", Topic.timeOffReason), wrap: true }]
+ }
+ ]
+ },
+ {
+ type: "TableRow",
+ cells: [
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: "Comments", weight: "Bolder" }]
+ },
+ {
+ type: "TableCell",
+ items: [{ type: "TextBlock", text: If(IsBlank(Topic.leaveComment), "—", Topic.leaveComment), wrap: true }]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "Container",
+ horizontalAlignment: "Right",
+ items: [
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "auto",
+ items: [
+ {
+ type: "Image",
+ url: Topic.WorkdayIconUrl,
+ style: "RoundedCorners",
+ size: "Small",
+ height: "20px",
+ width: "20px"
+ }
+ ],
+ verticalContentAlignment: "Center"
+ },
+ {
+ type: "Column",
+ width: "auto",
+ items: [
+ {
+ type: "TextBlock",
+ text: "Workday",
+ size: "Small",
+ color: "#242424",
+ weight: "Bolder"
+ }
+ ],
+ verticalContentAlignment: "Center",
+ spacing: "Small"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ actions: []
+ }
+
+ - kind: SendActivity
+ id: follow_up_msg
+ activity: Can I help you submit another request?
+
+ - kind: CancelAllDialogs
+ id: end_dialogs
+
+inputType:
+ properties:
+ EmployeeName:
+ displayName: EmployeeName
+ description: The name of the employee to fetch data for.
+ type: String
+
+ InputEndDate:
+ displayName: InputEndDate
+ description: The end date of the time off request.
+ type: DateTime
+
+ InputHoursPerDay:
+ displayName: InputHoursPerDay
+ description: The number of hours per day for the time off request.
+ type: Number
+
+ InputLeaveReason:
+ displayName: InputLeaveReason
+ description: Additional comments or context for the time off request.
+ type: String
+
+ InputStartDate:
+ displayName: InputStartDate
+ description: The start date of the time off request.
+ type: DateTime
+
+ InputTimeOffType:
+ displayName: InputTimeOffType
+ description: The type of time off being requested.
+ type: String
+
+outputType: {}
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeesviewtheirjobtaxonomy/msdyn_HRWorkdayHCMEmployeeGetJobTaxonomy.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeesviewtheirjobtaxonomy/msdyn_HRWorkdayHCMEmployeeGetJobTaxonomy.xml
new file mode 100644
index 00000000..627e54d8
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeesviewtheirjobtaxonomy/msdyn_HRWorkdayHCMEmployeeGetJobTaxonomy.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+ User
+
+ Template_GetWorkerRequest
+ Human_Resources
+ v42.0
+
+
+
+ //*[local-name()="Position_Title"]/text()
+ JobTitle
+
+
+ //*[local-name()="Business_Title"]/text()
+ BusinessTitle
+
+
+ //*[local-name()="Job_Profile_Name"]/text()
+ JobProfile
+
+
+ //*[local-name()='Job_Profile_Summary_Data']/*[local-name()='Job_Family_Reference']/*[local-name()='ID' and @*[local-name()='type']='Job_Family_ID']/text()
+ JobFamilyId
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeesviewtheirjobtaxonomy/topic.yaml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeesviewtheirjobtaxonomy/topic.yaml
new file mode 100644
index 00000000..dfb5b6b6
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayEmployeesviewtheirjobtaxonomy/topic.yaml
@@ -0,0 +1,144 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ Use this topic when the user asks about THEIR OWN job function / job family / job profile / job classification / job taxonomy / role taxonomy / internal title / external title / job code / job category stored in Workday. Data belongs exclusively to the requesting user.
+
+ Valid: "What is my external title?", "What's my internal title?", "What's my job function / job family / job profile / job category?", "Show me my job classification", "What is my job code?", "What's my role taxonomy?"
+
+ do NOT trigger for general HR policy or org-structure questions they are out of scope.
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - What is my job title?
+ - What job function do I belong to?
+ - What is my job function?
+ - What job category do I belong to?
+ - What is my role?
+ - What job Family do I belong to?
+ - What is my Job Profile?
+ - What is my internal title?
+ - What is my external title?
+
+ actions:
+ - kind: BeginDialog
+ id: 7R6DUf
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetJobTaxonomy
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: tLP86E
+ displayName: Parse job profiles
+ variable: Topic.workdayResponseTable
+ valueType:
+ kind: Record
+ properties:
+ BusinessTitle:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ JobFamilyId:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ JobProfile:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ JobTitle:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: setVariable_FRHCD9
+ variable: Topic.transformResponse
+ value: |-
+ ={
+ JobTitle: First(Topic.workdayResponseTable.JobTitle).Value,
+ BusinessTitle: First(Topic.workdayResponseTable.BusinessTitle).Value,
+ JobProfile: First(Topic.workdayResponseTable.JobProfile).Value,
+ JobFamilyId: First(Topic.workdayResponseTable.JobFamilyId).Value
+ }
+
+ - kind: BeginDialog
+ id: RZJGDY
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Job_Family_ID}"",""value"":""" & Topic.transformResponse.JobFamilyId & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetJobTaxonomyGeneric
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: rPHxhw
+ displayName: Parse Job Family Name
+ variable: Topic.jobFamilyTable
+ valueType:
+ kind: Record
+ properties:
+ JobFamily:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: setVariable_mS3r3D
+ displayName: Init Job Taxonomy Data
+ variable: Topic.jobFunctionData
+ value: |-
+ ={
+ EmployeeName: Global.ESS_UserContext_Employee_Firstname & " " & Global.ESS_UserContext_Employee_Lastname,
+ JobTitle: Topic.transformResponse.JobTitle,
+ JobProfile: Topic.transformResponse.JobProfile,
+ BusinessTitle: Topic.transformResponse.BusinessTitle,
+ JobFamily: First(Topic.jobFamilyTable.JobFamily).Value
+ }
+
+ - kind: SendActivity
+ id: sendActivity_AT4n5c
+ activity: |-
+ Here is your job information:
+ {Topic.jobFunctionData}
+
+outputType:
+ properties:
+ jobFunctionData:
+ displayName: jobFunctionData
+ type:
+ kind: Record
+ properties:
+ BusinessTitle: String
+ EmployeeName: String
+ JobFamily: String
+ JobProfile: String
+ JobTitle: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetContactInformation/msdyn_HRWorkdayHCMEmployeeGetHomeContactInformation.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetContactInformation/msdyn_HRWorkdayHCMEmployeeGetHomeContactInformation.xml
new file mode 100644
index 00000000..f218282a
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetContactInformation/msdyn_HRWorkdayHCMEmployeeGetHomeContactInformation.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+ User
+
+ msdyn_HRWorkdayHCMEmployeeGetHomeContactInformationRequest
+ Human_Resources
+ v42.0
+
+
+
+ //*[local-name()='Phone_Information_Data']
+ PhoneInformation
+
+
+ //*[local-name()='Email_Information_Data']
+ EmailInformation
+
+
+ //@*[local-name()='Formatted_Address']
+ HomeAddress
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+ {As_Of_Effective_Date}
+ 1
+ 999
+
+
+
+ false
+ false
+ false
+ false
+ false
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetContactInformation/topic.yaml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetContactInformation/topic.yaml
new file mode 100644
index 00000000..c687ef74
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetContactInformation/topic.yaml
@@ -0,0 +1,427 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ Use this topic when the user asks to VIEW or READ THEIR OWN contact information stored in Workday — home/personal address, mailing address, alternate address, personal email, work email, personal phone, mobile/cell phone, primary phone, secondary phone, work phone, home phone, emergency contact, or any contact-profile field. "Home" and "Personal" are synonymous.
+
+ Trigger on read-only queries about "my" contact info: "What is my address?", "Show my email on file", "What's my phone number in Workday?", "What contact info do I have?", "Show my mobile number", "What's my work email?", "Display my contact details".
+
+ Data is retrieved from Workday and belongs exclusively to the requesting user.
+
+ Do NOT trigger for:
+ - Another person's contact info
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: EmployeeName
+ description: The name of the employee whose data will be fetched
+ entity: PersonNamePrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - Show my Contact details?
+ - What is my Work Phone?
+ - What is my Work Email?
+ - What is my Work Address?
+ - Show my Work Contact information?
+ - Show my Personal Phone number?
+ - What is my Home Phone number?
+ - Show my Home Email?
+ - Can you tell me what my Personal Email is ?
+ - Which Personal email is registered as primary?
+ - Show my Home address?
+ - Show my Personal Contact Information?
+ - Show [EmployeeName]'s contact info
+
+ actions:
+ - kind: BeginDialog
+ id: uzpKxp
+ displayName: Check user access
+ input:
+ binding:
+ EmployeeName: =Topic.EmployeeName
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemAccessCheck
+
+ - kind: BeginDialog
+ id: Z6efSa
+ displayName: Fetch Work Contact Information from Workday
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: ="msdyn_HRWorkdayHCMEmployeeGetWorkContactInformation"
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: b90ZAp
+ displayName: Parse Work Contact data
+ variable: Topic.workContactData
+ valueType:
+ kind: Record
+ properties:
+ EmailInformation:
+ type:
+ kind: Table
+ properties:
+ Email_Data:
+ type:
+ kind: Record
+ properties:
+ Email_Address: String
+
+ Email_ID: String
+ Email_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Usage_Data:
+ type:
+ kind: Record
+ properties:
+ "@Public": String
+ Type_Data:
+ type:
+ kind: Record
+ properties:
+ "@Primary": String
+ Type_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ PhoneInformation:
+ type:
+ kind: Table
+ properties:
+ Phone_Data:
+ type:
+ kind: Record
+ properties:
+ "@Formatted_Phone": String
+ Complete_Phone_Number: String
+ Country_Code_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Device_Type_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Phone_ID: String
+ Phone_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Usage_Data:
+ type:
+ kind: Table
+ properties:
+ "@Public": String
+ Type_Data:
+ type:
+ kind: Record
+ properties:
+ "@Primary": String
+ Type_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ value: =Topic.workdayResponse
+
+ - kind: BeginDialog
+ id: tZ3bkQ
+ displayName: Fetch Home Contact Information from Workday
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: ="msdyn_HRWorkdayHCMEmployeeGetHomeContactInformation"
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: D15SKZ
+ displayName: Parse Home Contact data
+ variable: Topic.homeContactData
+ valueType:
+ kind: Record
+ properties:
+ EmailInformation:
+ type:
+ kind: Table
+ properties:
+ Email_Data:
+ type:
+ kind: Record
+ properties:
+ Email_Address: String
+ Email_Comment: String
+
+ Email_ID: String
+ Email_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Usage_Data:
+ type:
+ kind: Record
+ properties:
+ "@Public": String
+ Type_Data:
+ type:
+ kind: Record
+ properties:
+ "@Primary": String
+ Type_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ HomeAddress:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ PhoneInformation:
+ type:
+ kind: Table
+ properties:
+ Phone_Data:
+ type:
+ kind: Record
+ properties:
+ "@Formatted_Phone": String
+ Complete_Phone_Number: String
+ Country_Code_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Device_Type_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Phone_ID: String
+ Phone_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Usage_Data:
+ type:
+ kind: Table
+ properties:
+ "@Public": String
+ Type_Data:
+ type:
+ kind: Record
+ properties:
+ "@Primary": String
+ Type_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ value: =Topic.workdayResponse
+
+ - kind: BeginDialog
+ id: p2xAZU
+ displayName: Fetch Work Address
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: ="msdyn_HRWorkdayHCMEmployeeGetWorkAddress"
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: 3IdZeI
+ displayName: Parse Work Address
+ variable: Topic.workAddress
+ valueType:
+ kind: Record
+ properties:
+ WorkAddress:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: setVariable_LXnWnT
+ displayName: Set final data
+ variable: Topic.finalizedContactInformation
+ value: |-
+ ={
+ employeeName: Global.ESS_UserContext_Employee_Firstname & " " & Global.ESS_UserContext_Employee_Lastname,
+ workPhoneNumbers: ForAll(Topic.workContactData.PhoneInformation, {PhoneNumber: ThisRecord.Phone_Data.'@Formatted_Phone', IsPrimaryPhoneNumber: CountIf(ThisRecord.Usage_Data, Type_Data.'@Primary' = "1") > 0}),
+ workEmails: ForAll(Topic.workContactData.EmailInformation, {EmailAddress: ThisRecord.Email_Data.Email_Address, IsPrimaryEmailAddress: ThisRecord.Usage_Data.Type_Data.'@Primary'}),
+ workAddress: First(Topic.workAddress.WorkAddress).Value,
+ homePhoneNumbers: ForAll(Topic.homeContactData.PhoneInformation, {PhoneNumber: ThisRecord.Phone_Data.'@Formatted_Phone', IsPrimaryPhoneNumber: CountIf(ThisRecord.Usage_Data, Type_Data.'@Primary' = "1") > 0}),
+ homeEmails: ForAll(Topic.homeContactData.EmailInformation, {EmailAddress: ThisRecord.Email_Data.Email_Address, IsPrimaryEmailAddress: ThisRecord.Usage_Data.Type_Data.'@Primary'}),
+ homeAddress: First(Topic.homeContactData.HomeAddress).Value
+ }
+
+ - kind: SendActivity
+ id: sendActivity_Shc3jt
+ activity: |-
+ Here is your contact information:
+ {Topic.finalizedContactInformation}
+
+inputType:
+ properties:
+ EmployeeName:
+ displayName: EmployeeName
+ description: The name of the employee whose data will be fetched
+ type: String
+
+outputType:
+ properties:
+ finalizedContactInformation:
+ displayName: finalizedContactInformation
+ type:
+ kind: Record
+ properties:
+ employeeName: String
+ homeAddress: String
+ homeEmails:
+ type:
+ kind: Table
+ properties:
+ EmailAddress: String
+ IsPrimaryEmailAddress: String
+
+ homePhoneNumbers:
+ type:
+ kind: Table
+ properties:
+ IsPrimaryPhoneNumber: Boolean
+ PhoneNumber: String
+
+ workAddress: String
+ workEmails:
+ type:
+ kind: Table
+ properties:
+ EmailAddress: String
+ IsPrimaryEmailAddress: String
+
+ workPhoneNumbers:
+ type:
+ kind: Table
+ properties:
+ IsPrimaryPhoneNumber: Boolean
+ PhoneNumber: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetEducation/msdyn_HRWorkdayHCMEmployeeGetEducation.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetEducation/msdyn_HRWorkdayHCMEmployeeGetEducation.xml
new file mode 100644
index 00000000..d32311db
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetEducation/msdyn_HRWorkdayHCMEmployeeGetEducation.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+ User
+
+ HRWorkdayHCMEmployeeGetEducationRequest
+ Human_Resources
+ v41.0
+
+
+
+ //*[local-name()='Worker']/*[local-name()='Worker_Data']/*[local-name()='Qualification_Data']/*[local-name()='Education']
+ EducationData
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetEducation/topic.yaml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetEducation/topic.yaml
new file mode 100644
index 00000000..6fdd5ec0
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetEducation/topic.yaml
@@ -0,0 +1,239 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ Use this topic when the user asks to VIEW or READ THEIR OWN education / academic history / schooling stored in Workday — degree (bachelor's, master's, MBA, PhD), diploma, certificate, school/university/college/institution, alma mater, major, field of study, minor, graduation year/date, GPA, academic qualifications, or credentials.
+
+ Trigger on "my" education-related queries: "What's my education?", "Show my degree", "Where did I study?", "What university is on my profile?", "What's my major / field of study?", "Show my academic background / qualifications / credentials", "What schooling do I have on file?".
+
+ Data is retrieved from Workday and belongs exclusively to the requesting user.
+
+ Do NOT trigger for general questions about tuition reimbursement, learning programs, or training
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent: {}
+ actions:
+ - kind: BeginDialog
+ id: B7ae9T
+ displayName: Redirect to Get CommonExecution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetEducation
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: yJ0sSR
+ displayName: Parse Workday response
+ variable: Topic.parsedWorkdayResponse
+ valueType:
+ kind: Record
+ properties:
+ EducationData:
+ type:
+ kind: Table
+ properties:
+ Education_Data:
+ type:
+ kind: Table
+ properties:
+ Country_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Date_Degree_Received: String
+ Degree_Completed_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Degree_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Education_ID: String
+ Field_Of_Study_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ First_Year_Attended: String
+ Is_Highest_Level_of_Education: String
+ Last_Year_Attended: String
+ Location: String
+ School_Name: String
+ School_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Education_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ value: =Topic.workdayResponse
+
+ - kind: BeginDialog
+ id: M8G7as
+ displayName: Refresh country name reference data
+ input:
+ binding:
+ IsTableEmpty: =IsBlank(Global.CountryNameLookupTable)
+ ReferenceDataKey: ISO_3166-1_Alpha-2_Code
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemRefreshReferenceData
+
+ - kind: BeginDialog
+ id: iM5A5i
+ displayName: Refresh degree reference data
+ input:
+ binding:
+ IsTableEmpty: =IsBlank(Global.DegreeTypeLookupTable)
+ ReferenceDataKey: Degree_ID
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemRefreshReferenceData
+
+ - kind: BeginDialog
+ id: zG3du7
+ displayName: Refresh field of study reference data
+ input:
+ binding:
+ IsTableEmpty: =IsBlank(Global.FieldOfStudyLookupTable)
+ ReferenceDataKey: Field_Of_Study_ID
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemRefreshReferenceData
+
+ - kind: SetVariable
+ id: setVariable_nD5Gkb
+ displayName: Set final result data
+ variable: Topic.FinalizedEducationData
+ value: |
+ =ForAll(
+ Topic.parsedWorkdayResponse.EducationData,
+ ForAll(
+ ThisRecord.Education_Data,
+ With(
+ {currentRecord: ThisRecord},
+ {
+ EmployeeName: Global.ESS_UserContext_Employee_Firstname & " " & Global.ESS_UserContext_Employee_Lastname,
+ Country: LookUp(
+ Global.CountryNameLookupTable,
+ ID = LookUp(
+ currentRecord.Country_Reference.ID,
+ '@type' = First(Global.CountryNameLookupTable).Reference_ID_Type
+ ).'#text'
+ ).Referenced_Object_Descriptor,
+ School: currentRecord.School_Name,
+ Degree: LookUp(
+ Global.DegreeTypeLookupTable,
+ ID = LookUp(
+ currentRecord.Degree_Reference.ID,
+ '@type' = First(Global.DegreeTypeLookupTable).Reference_ID_Type
+ ).'#text'
+ ).Referenced_Object_Descriptor,
+ FieldOfStudy: LookUp(
+ Global.FieldOfStudyLookupTable,
+ ID = LookUp(
+ currentRecord.Field_Of_Study_Reference.ID,
+ '@type' = First(Global.FieldOfStudyLookupTable).Reference_ID_Type
+ ).'#text'
+ ).Referenced_Object_Descriptor,
+ FirstYearAttended: currentRecord.First_Year_Attended,
+ LastYearAttended: currentRecord.Last_Year_Attended
+ }
+ )
+ )
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_PLj70M
+ conditions:
+ - id: conditionItem_TCBTE7
+ condition: =IsBlank(Topic.parsedWorkdayResponse.EducationData)
+ actions:
+ - kind: SendActivity
+ id: sendActivity_jPX7sn
+ activity: There is no education information listed for you in the system. If this information should be included, you can update it directly in Workday or reach out to HR for assistance.
+
+ elseActions:
+ - kind: SendActivity
+ id: sendActivity_educationResponse
+ activity: |-
+ Here is your education information:
+ {Topic.FinalizedEducationData}
+
+inputType:
+ properties:
+ EmployeeName:
+ displayName: EmployeeName
+ description: The name of the person whose employment data is being requested.
+ type: String
+
+outputType:
+ properties:
+ FinalizedEducationData:
+ displayName: FinalizedEducationData
+ type:
+ kind: Table
+ properties:
+ Value:
+ type:
+ kind: Table
+ properties:
+ Country: String
+ Degree: String
+ EmployeeName: String
+ FieldOfStudy: String
+ FirstYearAttended: String
+ LastYearAttended: String
+ School: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetGovernmentIDs/msdyn_HRWorkdayHCMEmployeeGetGovernmentIds.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetGovernmentIDs/msdyn_HRWorkdayHCMEmployeeGetGovernmentIds.xml
new file mode 100644
index 00000000..3eca23e4
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetGovernmentIDs/msdyn_HRWorkdayHCMEmployeeGetGovernmentIds.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+ User
+
+ Template_GetPersonalInformationRequest
+ Human_Resources
+ v42.0
+
+
+
+ //*[local-name()='Government_ID']/*[local-name()='Government_ID_Data']
+ GovernmentId
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetGovernmentIDs/topic.yaml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetGovernmentIDs/topic.yaml
new file mode 100644
index 00000000..a4f13e65
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetGovernmentIDs/topic.yaml
@@ -0,0 +1,155 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ Use this topic when the user asks about THEIR OWN government-issued IDs / immigration documents / identity documents stored in Workday — Social Security Number (SSN), national ID, passport, driver's license, tax ID, PAN card, Aadhaar, National Insurance (NI) number, SIN, visa, work visa, work permit, work authorization, green card, permanent resident (PR) card, or related fields. Data belongs exclusively to the requesting user.
+
+ Trigger on "my" government-ID queries: "What is my SSN / PAN / Aadhaar / NI number?", "What's my passport number?", "When does my visa / work permit expire?", "Show my driver's license on file", "What's my tax ID?", "What government IDs are on my Workday profile?", "What's my green card / PR status?".
+
+ Do NOT trigger for general questions about ID applications, renewals, immigration law, or work authorization policy
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent: {}
+ actions:
+ - kind: BeginDialog
+ id: 3bFDxH
+ displayName: Redirect to Workday System Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetGovernmentIds
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: v0OBu2
+ displayName: Parse workdayResponse into table
+ variable: Topic.parsedWorkdayResponse
+ valueType:
+ kind: Record
+ properties:
+ GovernmentId:
+ type:
+ kind: Table
+ properties:
+ Country_Reference:
+ type:
+ kind: Record
+ properties:
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Expiration_Date: String
+ ID: String
+ ID_Type_Reference:
+ type:
+ kind: Record
+ properties:
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Issued_Date: String
+
+ value: =Topic.workdayResponse
+
+ - kind: BeginDialog
+ id: 6k3mYE
+ displayName: Refresh country name reference data
+ input:
+ binding:
+ IsTableEmpty: =IsBlank(Global.CountryNameLookupTable)
+ ReferenceDataKey: ISO_3166-1_Alpha-2_Code
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemRefreshReferenceData
+
+ - kind: BeginDialog
+ id: Xoydcu
+ displayName: Refresh government ID type data
+ input:
+ binding:
+ IsTableEmpty: =IsBlank(Global.GovernmentIdTypeLookupTable)
+ ReferenceDataKey: Government_ID_Type_ID
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemRefreshReferenceData
+
+ - kind: SetVariable
+ id: setVariable_ZYrAxQ
+ displayName: Get values from reference tables
+ variable: Topic.finalizedResponseTableData
+ value: |-
+ =ForAll(
+ Topic.parsedWorkdayResponse.GovernmentId,
+ With(
+ {currentRecord: ThisRecord},
+ {
+ EmployeeName: Global.ESS_UserContext_Employee_Firstname & " " & Global.ESS_UserContext_Employee_Lastname,
+ CountryName: LookUp(
+ Global.CountryNameLookupTable,
+ ID = LookUp(
+ currentRecord.Country_Reference.ID,
+ '@type' = First(Global.CountryNameLookupTable).Reference_ID_Type
+ ).'#text'
+ ).Referenced_Object_Descriptor,
+ ExpirationDate: currentRecord.Expiration_Date,
+ IDType: LookUp(
+ Global.GovernmentIdTypeLookupTable,
+ ID = LookUp(
+ currentRecord.ID_Type_Reference.ID,
+ '@type' = First(Global.GovernmentIdTypeLookupTable).Reference_ID_Type
+ ).'#text'
+ ).Referenced_Object_Descriptor,
+ IssuedDate: currentRecord.Issued_Date,
+ ID: ID
+ }
+ )
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_PLj70M
+ conditions:
+ - id: conditionItem_TCBTE7
+ condition: =IsBlank(Topic.parsedWorkdayResponse.GovernmentId)
+ actions:
+ - kind: SendActivity
+ id: sendActivity_jPX7sn
+ activity: There are no government IDs listed for you in the system. If this information should be included, you can update it directly in Workday or reach out to HR for assistance.
+
+ elseActions:
+ - kind: SendActivity
+ id: sendActivity_uxFMLZ
+ activity: |-
+ Here is your government ID information:
+ {Topic.finalizedResponseTableData}
+
+inputType:
+ properties:
+ EmployeeName:
+ displayName: EmployeeName
+ description: The name of the employee whose data is being requested.
+ type: String
+
+outputType:
+ properties:
+ finalizedResponseTableData:
+ displayName: finalizedResponseTableData
+ type:
+ kind: Table
+ properties:
+ CountryName: String
+ EmployeeName: String
+ ExpirationDate: String
+ ID: String
+ IDType: String
+ IssuedDate: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetUserProfile/README.md b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetUserProfile/README.md
new file mode 100644
index 00000000..ccb49768
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetUserProfile/README.md
@@ -0,0 +1,168 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Get User Profile
+
+This scenario enables employees to retrieve their profile information from Workday through a conversational interface. It provides comprehensive employee data including personal details, employment information, contact details, and tenure calculations.
+
+## Features
+
+- **Personal Information**: Name, Date of Birth, Gender
+- **Employment Details**: Employee ID, Business Title, Organization/Department, Manager, Location
+- **Contact Information**: Work Email, Work Phone, Home Email, Home Phone, Home Address
+- **Employment Status**: Active/Inactive status, Hire Date
+- **Tenure Calculation**: Continuous Service Date and calculated Length of Service (years, months, days)
+
+## Trigger Phrases
+
+Users can activate this topic with phrases like:
+- "What is my profile?"
+- "Show my profile"
+- "What is my employee ID?"
+- "What is my job title?"
+- "What is my work email?"
+- "Who is my manager?"
+- "What department am I in?"
+- "What is my tenure?"
+- "How long have I been with the company?"
+- "What is my hire date?"
+- "Am I an active employee?"
+
+## Files
+
+| File | Description |
+|------|-------------|
+| `topic.yaml` | Main Copilot Studio topic definition |
+| `msdyn_HRWorkdayHCMEmployeeGetUserProfile.xml` | XML template with XPath extractions for profile data |
+
+## Workday APIs Used
+
+| API | Service | Version | Purpose |
+|-----|---------|---------|---------|
+| `Get_Workers` | Human_Resources | v45.0 | Retrieve comprehensive employee profile data |
+
+## Data Retrieved
+
+The topic extracts the following fields from Workday:
+
+| Field | XPath Source | Description |
+|-------|--------------|-------------|
+| `EmployeeID` | Worker_Data/Worker_ID | Employee's Workday ID |
+| `Name` | Worker_Descriptor | Employee's full name |
+| `DOB` | Personal_Information_Data/Birth_Date | Date of birth |
+| `Gender` | Gender_Reference/@Descriptor | Gender |
+| `BusinessTitle` | Position_Data/Business_Title | Job title |
+| `Organization` | Organization_Reference (SUPERVISORY_ORGANIZATION) | Department/Org |
+| `Manager` | Manager_Reference/@Descriptor | Direct manager's name |
+| `Location` | Location_Reference/@Descriptor | Work location |
+| `HireDate` | Worker_Status_Data/Hire_Date | Original hire date |
+| `WorkEmail` | Email_Address (WORK usage type) | Work email address |
+| `HomeAddress` | Address_Data (HOME usage type) | Formatted home address |
+| `HomeEmail` | Email_Address (HOME usage type, primary) | Personal email |
+| `HomePhone` | Phone_Data (HOME usage type, primary) | Home phone number |
+| `WorkPhone` | Phone_Data (WORK usage type, primary) | Work phone number |
+| `Status` | Worker_Status_Data/Active | Employment status (Active/Inactive) |
+| `ContinuousServiceDate` | Worker_Status_Data/Continuous_Service_Date | Service start date |
+| `LengthOfService` | *Calculated* | Years, months, days of service |
+
+## Flow Overview
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ User Triggers Topic │
+│ (e.g., "What is my profile?") │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Call Get_Workers API │
+│ (with Employee_ID and As_Of_Date) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Parse Response via XPath │
+│ (Extract all profile fields) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Calculate Length of Service │
+│ (Years, Months, Days from Continuous Service Date) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Return finalizedData Record │
+│ (All fields available for AI to respond) │
+└─────────────────────────────────────────────────────────────┘
+```
+
+## Length of Service Calculation
+
+The topic automatically calculates the employee's length of service from their Continuous Service Date:
+
+```
+Years: RoundDown(DateDiff(ServiceDate, Today, Months) / 12, 0)
+Months: Mod(DateDiff(ServiceDate, Today, Months), 12)
+Days: DateDiff(AdjustedDate, Today, Days)
+```
+
+Output format: "X year(s) Y month(s) Z day(s)"
+
+## Dependencies
+
+This topic requires the following system topics/dialogs:
+- `msdyn_copilotforemployeeselfservicehr.topic.WorkdaySystemGetCommonExecution` - For executing Workday API calls
+
+## Global Variables Used
+
+| Variable | Description |
+|----------|-------------|
+| `Global.ESS_UserContext_Employee_Id` | The logged-in employee's Workday Employee ID |
+
+## Output
+
+The topic outputs a `finalizedData` record containing all profile fields that can be used by the AI orchestrator to formulate responses based on what the user specifically asked for.
+
+```yaml
+outputType:
+ properties:
+ finalizedData:
+ type: Record
+ properties:
+ EmployeeID: String
+ Name: String
+ DOB: String
+ Gender: String
+ BusinessTitle: String
+ Organization: String
+ Manager: String
+ Location: String
+ HireDate: String
+ WorkEmail: String
+ HomeAddress: String
+ HomeEmail: String
+ HomePhone: String
+ WorkPhone: String
+ Status: String
+ ContinuousServiceDate: String
+ LengthOfService: String
+```
+
+## Important Notes
+
+1. **Privacy**: This topic only returns data for the requesting user. Questions about other employees (managers, colleagues) are explicitly rejected per the model description.
+
+2. **Tenure Information**: Length of Service is only included in the AI's response when the user specifically asks about tenure, service length, or how long they've been with the company.
+
+3. **Status Conversion**: The raw `Active` field from Workday (1 or 0) is converted to human-readable "Active" or "Inactive".
+
+4. **Response Optimization**: The Get_Workers request is optimized to exclude unnecessary data (benefits, qualifications, photos, etc.) to improve performance.
+
+## Version History
+
+| Version | Date | Changes |
+|---------|------|---------|
+| 1.0 | December 2025 | Initial release with comprehensive profile retrieval |
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetUserProfile/msdyn_HRWorkdayHCMEmployeeGetUserProfile.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetUserProfile/msdyn_HRWorkdayHCMEmployeeGetUserProfile.xml
new file mode 100644
index 00000000..37412e3d
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetUserProfile/msdyn_HRWorkdayHCMEmployeeGetUserProfile.xml
@@ -0,0 +1,131 @@
+
+
+
+
+
+ User
+
+ Template_GetWorkerRequest
+ Human_Resources
+ v45.0
+
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Worker_ID']/text()
+ EmployeeID
+
+
+ //*[local-name()='Get_Workers_Response']/*[local-name()='Response_Data']/*[local-name()='Worker'][1]/*[local-name()='Worker_Descriptor']/text()
+ Name
+
+
+ //*[local-name()='Personal_Information_Data']/*[local-name()='Birth_Date']/text()
+ DOB
+
+
+ //*[local-name()='Personal_Information_For_Country_Data']/*[local-name()='Country_Personal_Information_Data']/*[local-name()='Gender_Reference']/@*[local-name()='Descriptor']
+ Gender
+
+
+ //*[local-name()='Position_Data']/*[local-name()='Business_Title']/text()
+ BusinessTitle
+
+
+ //*[local-name()='Worker_Organization_Data']/*[local-name()='Organization_Reference']/*[local-name()='ID'][@*[local-name()='type']='Organization_Reference_ID' and contains(text(),'SUPERVISORY_ORGANIZATION')]/../@*[local-name()='Descriptor']
+ Organization
+
+
+ //*[local-name()='Worker_Supervisory_Management_Chain_Data']/*[local-name()='Management_Chain_Data']/*[local-name()='Manager_Reference']/@*[local-name()='Descriptor']
+ Manager
+
+
+ //*[local-name()='Business_Site_Summary_Data']/*[local-name()='Location_Reference']/*[local-name()='ID'][@*[local-name()='type']='Location_ID']/../@*[local-name()='Descriptor']
+ Location
+
+
+ //*[local-name()='Worker_Status_Data']/*[local-name()='Hire_Date']/text()
+ HireDate
+
+
+ //*[local-name()='Email_Address_Data'][*[local-name()='Usage_Data']/*[local-name()='Type_Data']/*[local-name()='Type_Reference']/*[local-name()='ID'][@*[local-name()='type']='Communication_Usage_Type_ID' and text()='WORK']]/*[local-name()='Email_Address']/text()
+ WorkEmail
+
+
+ //*[local-name()='Address_Data'][*[local-name()='Usage_Data']/*[local-name()='Type_Data']/*[local-name()='Type_Reference']/*[local-name()='ID'][@*[local-name()='type']='Communication_Usage_Type_ID' and text()='HOME']]/@*[local-name()='Formatted_Address']
+ HomeAddress
+
+
+ //*[local-name()='Email_Address_Data'][*[local-name()='Usage_Data']/*[local-name()='Type_Data'][@*[local-name()='Primary']='1']/*[local-name()='Type_Reference']/*[local-name()='ID'][@*[local-name()='type']='Communication_Usage_Type_ID' and text()='HOME']]/*[local-name()='Email_Address']/text()
+ HomeEmail
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Contact_Data']/*[local-name()='Phone_Data'][*[local-name()='Usage_Data']/*[local-name()='Type_Data'][@*[local-name()='Primary']='1']/*[local-name()='Type_Reference']/*[local-name()='ID'][@*[local-name()='type']='Communication_Usage_Type_ID' and text()='HOME']]/@*[local-name()='Tenant_Formatted_Phone']
+ HomePhone
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Contact_Data']/*[local-name()='Phone_Data'][*[local-name()='Usage_Data']/*[local-name()='Type_Data'][@*[local-name()='Primary']='1']/*[local-name()='Type_Reference']/*[local-name()='ID'][@*[local-name()='type']='Communication_Usage_Type_ID' and text()='WORK']]/@*[local-name()='Tenant_Formatted_Phone']
+ WorkPhone
+
+
+ //*[local-name()='Worker_Status_Data']/*[local-name()='Active']/text()
+ Status
+
+
+ //*[local-name()='Worker_Status_Data']/*[local-name()='Continuous_Service_Date']/text()
+ ContinuousServiceDate
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ false
+ true
+ true
+ false
+ true
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+
+
+
+
+
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetUserProfile/topic.yaml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetUserProfile/topic.yaml
new file mode 100644
index 00000000..75ade8be
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGetUserProfile/topic.yaml
@@ -0,0 +1,240 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ Returns user's personal profile from Workday: identity, contact, and org info only.
+
+ Fields: Name, Emp ID, DOB, Gender, Title, Organization, Manager, Location, Email, Phone, Home Address.
+
+ DO NOT route here for: job level, grade, band, FTE, rehire date, exempt status, employment type, hire date
+
+ For FULL PROFILE:
+ Heading = ""Your employee profile""
+ Intro = ""Here's what I found in Workday, an HR platform your company uses.""
+ Sections: Personal information, Role and work.
+
+ For TENURE/POSITION:
+ Heading = ""Time in your position""
+ Intro = ""You've been in your current role as a [Position] for [LengthOfService].""
+ Sections: About your position
+
+ Rules:
+ - Large heading, sentence case, section divider below
+ - Bold section headings, non-bold sub bullets
+ - Address in one row, intro after heading
+ - Only relevant fields unless full profile requested"
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - What is my profile?
+ - Show my profile
+ - What is my employee ID?
+ - What is my job title?
+ - What is my work email?
+ - What is my manager's name?
+ - Who is my manager?
+ - What department am I in?
+ - What is my organization?
+ - What is my work phone number?
+ - What is my home address?
+ - What is my hire date?
+ - When did I start working here?
+ - What is my tenure?
+ - How long have I been with the company?
+ - What is my length of service?
+ - Am I an active employee?
+ - What is my employment status?
+ - What is my date of birth?
+ - What is my gender?
+ - What is my location?
+ - Where do I work?
+
+ actions:
+ - kind: BeginDialog
+ id: dZAI4Y
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetUserProfile
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: fP9fKn
+ displayName: Parse workdayResponse
+ variable: Topic.parsedWorkdayResponse
+ valueType:
+ kind: Record
+ properties:
+ EmployeeID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ Name:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ DOB:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ Gender:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ BusinessTitle:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ Organization:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ Manager:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ Location:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ HireDate:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ WorkEmail:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ HomeAddress:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ HomeEmail:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ HomePhone:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ WorkPhone:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ Status:
+ type:
+ kind: Table
+ properties:
+ Value: String
+ ContinuousServiceDate:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: setVariable_ServiceDate
+ displayName: Calculate service date values
+ variable: Topic.serviceDateCalc
+ value: =DateValue(First(Topic.parsedWorkdayResponse.ContinuousServiceDate).Value)
+
+ - kind: SetVariable
+ id: setVariable_Years
+ displayName: Calculate years
+ variable: Topic.yearsOfService
+ value: =RoundDown(DateDiff(Topic.serviceDateCalc, Today(), TimeUnit.Months) / 12, 0)
+
+ - kind: SetVariable
+ id: setVariable_Months
+ displayName: Calculate months
+ variable: Topic.monthsOfService
+ value: =Mod(DateDiff(Topic.serviceDateCalc, Today(), TimeUnit.Months), 12)
+
+ - kind: SetVariable
+ id: setVariable_Days
+ displayName: Calculate days
+ variable: Topic.daysOfService
+ value: =DateDiff(DateAdd(Topic.serviceDateCalc, DateDiff(Topic.serviceDateCalc, Today(), TimeUnit.Months), TimeUnit.Months), Today(), TimeUnit.Days)
+
+ - kind: SetVariable
+ id: setVariable_LHTcFu
+ displayName: Set finalized data
+ variable: Topic.finalizedData
+ value: |-
+ ={
+ EmployeeID: First(Topic.parsedWorkdayResponse.EmployeeID).Value,
+ Name: First(Topic.parsedWorkdayResponse.Name).Value,
+ DOB: First(Topic.parsedWorkdayResponse.DOB).Value,
+ Gender: First(Topic.parsedWorkdayResponse.Gender).Value,
+ BusinessTitle: First(Topic.parsedWorkdayResponse.BusinessTitle).Value,
+ Organization: First(Topic.parsedWorkdayResponse.Organization).Value,
+ Manager: First(Topic.parsedWorkdayResponse.Manager).Value,
+ Location: First(Topic.parsedWorkdayResponse.Location).Value,
+ HireDate: First(Topic.parsedWorkdayResponse.HireDate).Value,
+ WorkEmail: First(Topic.parsedWorkdayResponse.WorkEmail).Value,
+ HomeAddress: Substitute(Substitute(Concat(Topic.parsedWorkdayResponse.HomeAddress, Value, ", "), Char(10), ", "), Char(13), ""),
+ HomeEmail: First(Topic.parsedWorkdayResponse.HomeEmail).Value,
+ HomePhone: First(Topic.parsedWorkdayResponse.HomePhone).Value,
+ WorkPhone: First(Topic.parsedWorkdayResponse.WorkPhone).Value,
+ Status: If(First(Topic.parsedWorkdayResponse.Status).Value = "1", "Active", "Inactive"),
+ ContinuousServiceDate: First(Topic.parsedWorkdayResponse.ContinuousServiceDate).Value,
+ LengthOfService:
+ If(Topic.yearsOfService > 0, Topic.yearsOfService & " year" & If(Topic.yearsOfService > 1, "s", "") & " ", "") &
+ If(Topic.monthsOfService > 0, Topic.monthsOfService & " month" & If(Topic.monthsOfService > 1, "s", "") & " ", "") &
+ Topic.daysOfService & " day" & If(Topic.daysOfService <> 1, "s", "")
+ }
+
+ - kind: SendActivity
+ id: sendActivity_0SDSlB
+ activity: |-
+ Here is your employee profile:
+ {Topic.finalizedData}
+
+inputType: {}
+outputType:
+ properties:
+ finalizedData:
+ displayName: finalizedData
+ type:
+ kind: Record
+ properties:
+ EmployeeID: String
+ Name: String
+ DOB: String
+ Gender: String
+ BusinessTitle: String
+ Organization: String
+ Manager: String
+ Location: String
+ HireDate: String
+ WorkEmail: String
+ HomeAddress: String
+ HomeEmail: String
+ HomePhone: String
+ WorkPhone: String
+ Status: String
+ ContinuousServiceDate: String
+ LengthOfService: String
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGivePeerFeedback/msdyn_HRWorkdayHCMEmployeeGiveFeedback.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGivePeerFeedback/msdyn_HRWorkdayHCMEmployeeGiveFeedback.xml
new file mode 100644
index 00000000..6d30bdaf
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGivePeerFeedback/msdyn_HRWorkdayHCMEmployeeGiveFeedback.xml
@@ -0,0 +1,60 @@
+
+
+
+
+ User
+
+ msdyn_HRWorkdayHCMEmployeeGiveFeedback
+ Talent
+ v42.0
+
+
+
+ Employee_ID
+ {Employee_ID}
+
+
+ Employee_ID_Of_Receiver
+ {Employee_ID_Of_Receiver}
+
+
+ Comment
+ {Comment}
+
+
+
+
+ WID
+
+ //*[local-name()='Give_Feedback_Response']
+ /*[local-name()='Give_Feedback_Event_Reference']
+ /*[local-name()='ID'][@*[local-name()='type']='WID']/text()
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+
+
+
+ {Employee_ID}
+
+
+ {Employee_ID_Of_Receiver}
+
+ {Comment}
+ true
+ false
+ false
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGivePeerFeedback/topic.yaml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGivePeerFeedback/topic.yaml
new file mode 100644
index 00000000..1f1ea0d3
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayGivePeerFeedback/topic.yaml
@@ -0,0 +1,83 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ This topic provides a way to send feedback to the coworkers.
+
+ Some of the valid requests are as follows:
+
+ give feedback
+ share feedback for a colleague
+ submit peer feedback
+ feedback for employee
+ I want to appreciate my teammate
+ report feedback about coworker
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - send feedback about workday
+ - give feedback on workday
+ - workday feedback
+ - report issue with workday
+ - share thoughts on workday
+ - submit comments for workday
+ - provide input on workday experience
+ - suggest improvements for workday
+ - complain about workday
+
+ actions:
+ - kind: Question
+ id: Question_KkmuHF
+ variable: Topic.varEmployeeId
+ prompt: What is the _Employee ID_ of the colleague you want to give feedback for?
+ entity: StringPrebuiltEntity
+
+ - kind: ConditionGroup
+ id: ConditionGroup_8NOr8O
+ conditions:
+ - id: ConditionItem_GLZBTd
+ condition: =!IsBlank(Topic.varEmployeeId)
+ actions:
+ - kind: Question
+ id: Question_ZGYUYN
+ variable: Topic.FeedbackText
+ prompt: Please enter your feedback for your coworker.
+ entity: StringPrebuiltEntity
+
+ - kind: BeginDialog
+ id: Za15eH
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """}, {""key"":""{Employee_ID_Of_Receiver}"",""value"":"""& Topic.varEmployeeId & """}, {""key"":""{Comment}"",""value"":""" & Topic.FeedbackText & """}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGiveFeedback
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ConditionGroup
+ id: conditionGroup_r9LEup
+ conditions:
+ - id: conditionItem_XVznZc
+ condition: =IsBlank(Topic.errorResponse) && Topic.isSuccess = true
+ actions:
+ - kind: SendActivity
+ id: sendActivity_ISzuar
+ activity: Feedback is sent successfully
+
+ elseActions:
+ - kind: SendActivity
+ id: sendActivity_qhWHOF
+ activity: There is a failure in sending the feedback {Topic.errorResponse}
+
+ elseActions:
+ - kind: SendActivity
+ id: SendActivity_jLD6lX
+ activity: You cannot send feedback to yourself.
+
+inputType: {}
+outputType: {}
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/README.md b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/README.md
new file mode 100644
index 00000000..2a518385
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/README.md
@@ -0,0 +1,104 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Manage Emergency Contact
+
+## Overview
+
+This topic enables employees to manage their emergency contacts in Workday through a conversational interface. Employees can view existing contacts, add new emergency contacts, update existing ones, and delete contacts they no longer need.
+
+## Features
+
+- View existing emergency contacts with primary contact highlighted
+- Add new emergency contacts with full details
+- Update details of existing emergency contacts
+- Delete non-primary emergency contacts
+- Set or change the primary emergency contact
+- Assign priority levels (2-10) to contacts
+
+## Snapshots
+
+
+
+
+
+## Trigger Phrases
+
+- "Manage my emergency contacts"
+- "Update my emergency contact"
+- "Add emergency contact"
+- "Change my emergency contact information"
+- "Delete my emergency contact"
+- "Show my emergency contacts"
+
+## Files
+
+| File | Description |
+|------|-------------|
+| `topic.yaml` | Copilot Studio topic definition with conversation flow |
+| `msdyn_HRWorkdayHCMEmployeeGetEmergencyContactInfo.xml` | XML template to fetch existing emergency contacts |
+| `msdyn_HRWorkdayHCMEmployeeAddEmergencyContact.xml` | XML template to add a new emergency contact |
+| `msdyn_HRWorkdayHCMEmployeeUpdateEmergencyContact.xml` | XML template to update an existing emergency contact |
+| `msdyn_HRWorkdayDeleteEmergencyContact.xml` | XML template to delete an emergency contact |
+
+## Workday APIs Used
+
+| API | Purpose |
+|-----|---------|
+| `Get_Workers` | Retrieve employee's existing emergency contacts |
+| `Change_Emergency_Contacts` | Add, update, or delete emergency contact information |
+
+## Flow Overview
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ User Triggers Topic │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Fetch Reference Data (Country Codes, Relationships) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Fetch Existing Emergency Contacts │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Show Selection Card (or Go to Add if no contacts) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Show Add/Update Form (Adaptive Card) │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Submit to Workday │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────┐
+│ Show Success/Error Message │
+└─────────────────────────────────────────────────────────────┘
+```
+
+## Configurations
+
+Environment makers need to configure the following in the topic:
+
+| Configuration | Description | Location in Topic |
+|---------------|-------------|-------------------|
+| **Relationship Types** | Available relationship options from Workday reference data | Dynamic from GetReferenceData |
+| **Country Codes** | Phone country codes from Workday reference data | Dynamic from GetReferenceData |
+| **States/Provinces** | Available state/province codes per country | Adaptive card dropdown |
+| **Workday Icon** | Update the icon URL to match your organization's branding | Topic properties > Icon |
+| **Workday URL** | Set your organization's Workday tenant URL | HTTP action or connector configuration |
+
+## Dependencies
+
+- **Employee Context**: Worker ID must be available in the conversation context
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayDeleteEmergencyContact.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayDeleteEmergencyContact.xml
new file mode 100644
index 00000000..0a762798
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayDeleteEmergencyContact.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+ User
+
+ Template_DeleteEmergencyContactRequest
+ Human_Resources
+ v45.0
+
+
+
+ //*[local-name()='Emergency_Contact_Event_Reference']/*[local-name()='ID'][@*[local-name()='type']='WID']/text()
+ EventID
+
+
+
+
+
+
+
+
+
+ false
+ true
+ true
+
+ {Comment}
+
+ {Employee_ID}
+
+
+
+
+
+ {Employee_ID}
+
+ false
+
+
+ {Emergency_Contact_WID}
+
+ true
+
+ false
+ {Priority}
+
+ {Relationship_Type}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeAddEmergencyContact.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeAddEmergencyContact.xml
new file mode 100644
index 00000000..8d4bcf05
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeAddEmergencyContact.xml
@@ -0,0 +1,101 @@
+
+
+
+
+ User
+
+ Template_AddEmergencyContactRequest
+ Human_Resources
+ v45.0
+
+
+
+ //*[local-name()='Emergency_Contact_Event_Reference']/*[local-name()='ID'][@*[local-name()='type']='WID']/text()
+ EventID
+
+
+
+
+
+
+
+
+
+ true
+ true
+ true
+
+ {Comment}
+
+ {Employee_ID}
+
+
+
+
+
+ {Employee_ID}
+
+ false
+
+ false
+
+ {Primary}
+ {Priority}
+
+ {Relationship_Type}
+
+
+
+
+
+
+ {Country_Code}
+
+ {First_Name}
+ {Last_Name}
+
+
+
+
+
+
+ {Address_Country_Code}
+
+ {Address_Line_1}
+ {City}
+
+ {State_Province}
+
+ {Postal_Code}
+
+
+
+ HOME
+
+
+
+
+
+ {Address_Country_Code}
+ {Phone_Country_Code}
+ {Phone_Number}
+
+ {Phone_Device_Type}
+
+
+
+
+ HOME
+
+
+
+ =
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeGetEmergencyContactInfo.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeGetEmergencyContactInfo.xml
new file mode 100644
index 00000000..eb8c0af2
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeGetEmergencyContactInfo.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+ User
+
+ Template_GetEmergencyContactInfoRequest
+ Human_Resources
+ v45.0
+
+
+
+ //*[local-name()='Related_Person_Data']/*[local-name()='Related_Person'][*[local-name()='Emergency_Contact']]
+ EmergencyContacts
+
+
+
+
+
+
+
+
+
+
+ {Employee_ID}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+
+
+
+
+
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeUpdateEmergencyContact.xml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeUpdateEmergencyContact.xml
new file mode 100644
index 00000000..e6928dc4
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/msdyn_HRWorkdayHCMEmployeeUpdateEmergencyContact.xml
@@ -0,0 +1,105 @@
+
+
+
+
+
+ User
+
+ Template_UpdateEmergencyContactRequest
+ Human_Resources
+ v45.0
+
+
+
+ //*[local-name()='Emergency_Contact_Event_Reference']/*[local-name()='ID'][@*[local-name()='type']='WID']/text()
+ EventID
+
+
+
+
+
+
+
+
+
+ false
+ true
+ true
+
+ {Comment}
+
+ {Employee_ID}
+
+
+
+
+
+ {Employee_ID}
+
+ false
+
+
+ {Emergency_Contact_WID}
+
+ false
+
+ {Primary}
+ {Priority}
+
+ {Relationship_Type}
+
+
+
+
+
+
+ {Country_Code}
+
+ {First_Name}
+ {Last_Name}
+
+
+
+
+
+
+ {Address_Country_Code}
+
+ {Address_Line_1}
+ {City}
+
+ {State_Province}
+
+ {Postal_Code}
+
+
+
+ HOME
+
+
+
+
+
+ {Address_Country_Code}
+ {Phone_Country_Code}
+ {Phone_Number}
+
+ {Phone_Device_Type}
+
+
+
+
+ HOME
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/topic.yaml b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/topic.yaml
new file mode 100644
index 00000000..0e39e720
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/topic.yaml
@@ -0,0 +1,1378 @@
+kind: AdaptiveDialog
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: InputAction
+ description: "Look for keywords 'add', 'new', or 'create' in the user's request. If found, extract 'add'. Look for keywords 'get', 'view', 'show', 'list', 'see', 'display', or 'what are' in the user's request. If found, extract 'get'. Look for keywords 'delete' or 'remove' in the user's request. If found, extract 'delete'. Otherwise extract 'manage'."
+ entity: StringPrebuiltEntity
+ shouldPromptUser: false
+
+modelDescription: |-
+ Use this topic when the user wants to MANAGE — ADD, UPDATE, CHANGE, MODIFY, EDIT, REPLACE, or REMOVE — THEIR OWN emergency contact / next of kin / ICE (in case of emergency) contact in Workday. Includes adding a new emergency contact and updating an existing one.
+
+ Triggers:
+ - "Manage my emergency contacts"
+ - "Update my emergency contact"
+ - "Add or update emergency contact"
+ - "Change my emergency contact information"
+ - "Add a new emergency contact"
+ - "Update emergency contact name / phone / email / relationship"
+ - "Remove an emergency contact"
+ - "Set up my next of kin"
+
+ Data is submitted to Workday and belongs exclusively to the requesting user.
+
+ Do NOT trigger for general questions about emergency procedures or company safety policies
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent: {}
+ actions:
+ - kind: SetVariable
+ id: set_workday_icon_url_2
+ variable: Topic.WorkdayIconUrl
+ value: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=
+
+ - kind: ConditionGroup
+ id: check_country_codes
+ conditions:
+ - id: country_codes_uninitialized
+ condition: =IsBlank(Global.CountryCodeLookupTable)
+ displayName: If country code picklist is uninitialized
+ actions:
+ - kind: BeginDialog
+ id: fetch_country_codes
+ displayName: Redirect to Workday System Get Reference Data
+ input:
+ binding:
+ referenceDataKey: Country_Phone_Code_ID
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.GetReferenceData
+
+ - kind: ConditionGroup
+ id: check_relationship_types
+ conditions:
+ - id: relationship_types_uninitialized
+ condition: =IsBlank(Global.RelatedPersonRelationshipLookupTable)
+ displayName: If relationship type picklist is uninitialized
+ actions:
+ - kind: BeginDialog
+ id: fetch_relationship_types
+ displayName: Redirect to Workday System Get Reference Data
+ input:
+ binding:
+ referenceDataKey: Related_Person_Relationship_ID
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.GetReferenceData
+
+ - kind: ConditionGroup
+ id: check_state_province_choices
+ conditions:
+ - id: state_choices_uninitialized
+ condition: =IsBlank(Global.StateProvinceChoices)
+ displayName: If state/province choices are uninitialized
+ actions:
+ - kind: SetVariable
+ id: init_state_province_choices
+ variable: Global.StateProvinceChoices
+ value: |
+ =[
+ { title: "Alabama", value: "USA-AL", country: "USA" },
+ { title: "Alaska", value: "USA-AK", country: "USA" },
+ { title: "Arizona", value: "USA-AZ", country: "USA" },
+ { title: "Arkansas", value: "USA-AR", country: "USA" },
+ { title: "California", value: "USA-CA", country: "USA" },
+ { title: "Colorado", value: "USA-CO", country: "USA" },
+ { title: "Connecticut", value: "USA-CT", country: "USA" },
+ { title: "Delaware", value: "USA-DE", country: "USA" },
+ { title: "Florida", value: "USA-FL", country: "USA" },
+ { title: "Georgia", value: "USA-GA", country: "USA" },
+ { title: "Hawaii", value: "USA-HI", country: "USA" },
+ { title: "Idaho", value: "USA-ID", country: "USA" },
+ { title: "Illinois", value: "USA-IL", country: "USA" },
+ { title: "Indiana", value: "USA-IN", country: "USA" },
+ { title: "Iowa", value: "USA-IA", country: "USA" },
+ { title: "Kansas", value: "USA-KS", country: "USA" },
+ { title: "Kentucky", value: "USA-KY", country: "USA" },
+ { title: "Louisiana", value: "USA-LA", country: "USA" },
+ { title: "Maine", value: "USA-ME", country: "USA" },
+ { title: "Maryland", value: "USA-MD", country: "USA" },
+ { title: "Massachusetts", value: "USA-MA", country: "USA" },
+ { title: "Michigan", value: "USA-MI", country: "USA" },
+ { title: "Minnesota", value: "USA-MN", country: "USA" },
+ { title: "Mississippi", value: "USA-MS", country: "USA" },
+ { title: "Missouri", value: "USA-MO", country: "USA" },
+ { title: "Montana", value: "USA-MT", country: "USA" },
+ { title: "Nebraska", value: "USA-NE", country: "USA" },
+ { title: "Nevada", value: "USA-NV", country: "USA" },
+ { title: "New Hampshire", value: "USA-NH", country: "USA" },
+ { title: "New Jersey", value: "USA-NJ", country: "USA" },
+ { title: "New Mexico", value: "USA-NM", country: "USA" },
+ { title: "New York", value: "USA-NY", country: "USA" },
+ { title: "North Carolina", value: "USA-NC", country: "USA" },
+ { title: "North Dakota", value: "USA-ND", country: "USA" },
+ { title: "Ohio", value: "USA-OH", country: "USA" },
+ { title: "Oklahoma", value: "USA-OK", country: "USA" },
+ { title: "Oregon", value: "USA-OR", country: "USA" },
+ { title: "Pennsylvania", value: "USA-PA", country: "USA" },
+ { title: "Rhode Island", value: "USA-RI", country: "USA" },
+ { title: "South Carolina", value: "USA-SC", country: "USA" },
+ { title: "South Dakota", value: "USA-SD", country: "USA" },
+ { title: "Tennessee", value: "USA-TN", country: "USA" },
+ { title: "Texas", value: "USA-TX", country: "USA" },
+ { title: "Utah", value: "USA-UT", country: "USA" },
+ { title: "Vermont", value: "USA-VT", country: "USA" },
+ { title: "Virginia", value: "USA-VA", country: "USA" },
+ { title: "Washington", value: "USA-WA", country: "USA" },
+ { title: "West Virginia", value: "USA-WV", country: "USA" },
+ { title: "Wisconsin", value: "USA-WI", country: "USA" },
+ { title: "Wyoming", value: "USA-WY", country: "USA" },
+ { title: "District of Columbia", value: "USA-DC", country: "USA" },
+ { title: "Alberta", value: "CA-AB", country: "CAN" },
+ { title: "British Columbia", value: "CA-BC", country: "CAN" },
+ { title: "Manitoba", value: "CA-MB", country: "CAN" },
+ { title: "New Brunswick", value: "CA-NB", country: "CAN" },
+ { title: "Newfoundland and Labrador", value: "CA-NL", country: "CAN" },
+ { title: "Northwest Territories", value: "CA-NT", country: "CAN" },
+ { title: "Nova Scotia", value: "CA-NS", country: "CAN" },
+ { title: "Nunavut", value: "CA-NU", country: "CAN" },
+ { title: "Ontario", value: "CA-ON", country: "CAN" },
+ { title: "Prince Edward Island", value: "CA-PE", country: "CAN" },
+ { title: "Quebec", value: "CA-QC", country: "CAN" },
+ { title: "Saskatchewan", value: "CA-SK", country: "CAN" },
+ { title: "Yukon", value: "CA-YT", country: "CAN" },
+ { title: "England", value: "GBR-ENG", country: "GBR" },
+ { title: "Scotland", value: "GBR-SCT", country: "GBR" },
+ { title: "Wales", value: "GBR-WLS", country: "GBR" },
+ { title: "Northern Ireland", value: "GBR-NIR", country: "GBR" },
+ { title: "Andaman and Nicobar Islands", value: "IN-AN", country: "IND" },
+ { title: "Andhra Pradesh", value: "IN-AP", country: "IND" },
+ { title: "Arunachal Pradesh", value: "IN-AR", country: "IND" },
+ { title: "Assam", value: "IN-AS", country: "IND" },
+ { title: "Bihar", value: "IN-BR", country: "IND" },
+ { title: "Chandigarh", value: "IN-CH", country: "IND" },
+ { title: "Chhattisgarh", value: "IN-CG", country: "IND" },
+ { title: "Dadra and Nagar Haveli", value: "IN-DN", country: "IND" },
+ { title: "Daman and Diu", value: "IN-DD", country: "IND" },
+ { title: "Delhi", value: "IN-DL", country: "IND" },
+ { title: "Goa", value: "IN-GA", country: "IND" },
+ { title: "Gujarat", value: "IN-GJ", country: "IND" },
+ { title: "Haryana", value: "IN-HR", country: "IND" },
+ { title: "Himachal Pradesh", value: "IN-HP", country: "IND" },
+ { title: "Jammu and Kashmir", value: "IN-JK", country: "IND" },
+ { title: "Jharkhand", value: "IN-JH", country: "IND" },
+ { title: "Karnataka", value: "IN-KA", country: "IND" },
+ { title: "Kerala", value: "IN-KL", country: "IND" },
+ { title: "Ladakh", value: "IN-LA", country: "IND" },
+ { title: "Lakshadweep", value: "IN-LD", country: "IND" },
+ { title: "Madhya Pradesh", value: "IN-MP", country: "IND" },
+ { title: "Maharashtra", value: "IN-MH", country: "IND" },
+ { title: "Manipur", value: "IN-MN", country: "IND" },
+ { title: "Meghalaya", value: "IN-ML", country: "IND" },
+ { title: "Mizoram", value: "IN-MZ", country: "IND" },
+ { title: "Nagaland", value: "IN-NL", country: "IND" },
+ { title: "Odisha", value: "IN-OD", country: "IND" },
+ { title: "Puducherry", value: "IN-PY", country: "IND" },
+ { title: "Punjab", value: "IN-PB", country: "IND" },
+ { title: "Rajasthan", value: "IN-RJ", country: "IND" },
+ { title: "Sikkim", value: "IN-SK", country: "IND" },
+ { title: "Tamil Nadu", value: "IN-TN", country: "IND" },
+ { title: "Telangana", value: "IN-TS", country: "IND" },
+ { title: "Tripura", value: "IN-TR", country: "IND" },
+ { title: "Uttar Pradesh", value: "IN-UP", country: "IND" },
+ { title: "Uttarakhand", value: "IN-UK", country: "IND" },
+ { title: "West Bengal", value: "IN-WB", country: "IND" },
+ { title: "Australian Capital Territory", value: "AU-ACT", country: "AUS" },
+ { title: "New South Wales", value: "AU-NSW", country: "AUS" },
+ { title: "Northern Territory", value: "AU-NT", country: "AUS" },
+ { title: "Queensland", value: "AU-QLD", country: "AUS" },
+ { title: "South Australia", value: "AU-SA", country: "AUS" },
+ { title: "Tasmania", value: "AU-TAS", country: "AUS" },
+ { title: "Victoria", value: "AU-VIC", country: "AUS" },
+ { title: "Western Australia", value: "AU-WA", country: "AUS" }
+ ]
+
+ # Detect if user wants to view contacts only (get/view/show/list)
+ - kind: SetVariable
+ id: set_is_view_only
+ variable: Topic.isViewOnly
+ value: =(!IsBlank(Topic.InputAction) && "get" in Lower(Topic.InputAction)) || "get" in Lower(System.Activity.Text) || "view" in Lower(System.Activity.Text) || "show" in Lower(System.Activity.Text) || "list" in Lower(System.Activity.Text) || "see my" in Lower(System.Activity.Text) || "what are" in Lower(System.Activity.Text) || "display" in Lower(System.Activity.Text)
+
+ # Detect if user wants to delete a contact
+ - kind: SetVariable
+ id: set_is_delete_mode
+ variable: Topic.isDeleteIntent
+ value: =(!IsBlank(Topic.InputAction) && "delete" in Lower(Topic.InputAction)) || "delete" in Lower(System.Activity.Text) || "remove" in Lower(System.Activity.Text)
+
+ # Check if user explicitly wants to add a new contact - skip loading existing contacts
+ - kind: ConditionGroup
+ id: check_direct_add
+ conditions:
+ - id: user_wants_add_directly
+ condition: =Topic.isViewOnly <> true && ((!IsBlank(Topic.InputAction) && "add" in Lower(Topic.InputAction)) || "add" in Lower(System.Activity.Text) || "new emergency contact" in Lower(System.Activity.Text) || "create" in Lower(System.Activity.Text))
+ displayName: User wants to add a new contact directly
+ actions:
+ - kind: SetVariable
+ id: set_add_mode_direct
+ variable: Topic.isUpdateMode
+ value: =false
+ - kind: SendActivity
+ id: direct_add_msg
+ activity: Sure, I can help you add a new emergency contact.
+ - kind: GotoAction
+ id: goto_country_selection_direct
+ actionId: country_selection_card
+
+ - kind: BeginDialog
+ id: fetch_emergency_contacts
+ displayName: Fetch existing emergency contacts
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeGetEmergencyContactInfo
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.fetchErrorResponse
+ isSuccess: Topic.fetchIsSuccess
+ workdayResponse: Topic.existingContactsResponse
+
+ - kind: ParseValue
+ id: parse_contacts
+ displayName: Parse emergency contacts response
+ variable: Topic.parsedContacts
+ valueType:
+ kind: Record
+ properties:
+ EmergencyContacts:
+ type:
+ kind: Table
+ properties:
+ Emergency_Contact:
+ type:
+ kind: Record
+ properties:
+ Emergency_Contact_Data:
+ type:
+ kind: Record
+ properties:
+ "@Primary": String
+ "@Priority": String
+ Emergency_Contact_ID: String
+
+ Emergency_Contact_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Person_Reference:
+ type:
+ kind: Record
+ properties:
+ "@Descriptor": String
+
+ Personal_Data:
+ type:
+ kind: Record
+ properties:
+ Contact_Data:
+ type:
+ kind: Record
+ properties:
+ Address_Data:
+ type:
+ kind: Table
+ properties:
+ "@Formatted_Address": String
+ Address_Line_Data:
+ type:
+ kind: Record
+ properties:
+ "#text": String
+
+ Country_Reference:
+ type:
+ kind: Record
+ properties:
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Country_Region_Reference:
+ type:
+ kind: Record
+ properties:
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ Municipality: String
+ Postal_Code: String
+
+ Phone_Data:
+ type:
+ kind: Record
+ properties:
+ "@Tenant_Formatted_Phone": String
+ International_Phone_Code: String
+ Phone_Number: String
+
+ Name_Data:
+ type:
+ kind: Record
+ properties:
+ Legal_Name_Data:
+ type:
+ kind: Record
+ properties:
+ Name_Detail_Data:
+ type:
+ kind: Record
+ properties:
+ "@Formatted_Name": String
+ First_Name: String
+ Last_Name: String
+
+ Related_Person_Relationship_Reference:
+ type:
+ kind: Table
+ properties:
+ "@Descriptor": String
+ ID:
+ type:
+ kind: Table
+ properties:
+ "@type": String
+ "#text": String
+
+ value: =Topic.existingContactsResponse
+
+ - kind: SetVariable
+ id: set_contact_list
+ displayName: Transform contacts for display
+ variable: Topic.contactSelectionList
+ value: |
+ =ForAll(
+ Filter(
+ Topic.parsedContacts.EmergencyContacts,
+ !IsBlank(LookUp(ThisRecord.Emergency_Contact.Emergency_Contact_Reference.ID, '@type' = "WID").'#text')
+ ),
+ {
+ DisplayName: ThisRecord.Personal_Data.Name_Data.Legal_Name_Data.Name_Detail_Data.'@Formatted_Name',
+ FirstName: ThisRecord.Personal_Data.Name_Data.Legal_Name_Data.Name_Detail_Data.First_Name,
+ LastName: ThisRecord.Personal_Data.Name_Data.Legal_Name_Data.Name_Detail_Data.Last_Name,
+ Phone: ThisRecord.Personal_Data.Contact_Data.Phone_Data.'@Tenant_Formatted_Phone',
+ PhoneNumber: ThisRecord.Personal_Data.Contact_Data.Phone_Data.Phone_Number,
+ PhoneCountryCode: ThisRecord.Personal_Data.Contact_Data.Phone_Data.International_Phone_Code,
+ Address: First(ThisRecord.Personal_Data.Contact_Data.Address_Data).'@Formatted_Address',
+ AddressLine1: First(ThisRecord.Personal_Data.Contact_Data.Address_Data).Address_Line_Data.'#text',
+ City: First(ThisRecord.Personal_Data.Contact_Data.Address_Data).Municipality,
+ PostalCode: First(ThisRecord.Personal_Data.Contact_Data.Address_Data).Postal_Code,
+ StateProvince: LookUp(First(ThisRecord.Personal_Data.Contact_Data.Address_Data).Country_Region_Reference.ID, '@type' = "Country_Region_ID").'#text',
+ CountryCode: LookUp(First(ThisRecord.Personal_Data.Contact_Data.Address_Data).Country_Reference.ID, '@type' = "ISO_3166-1_Alpha-3_Code").'#text',
+ RelationshipType: First(ThisRecord.Related_Person_Relationship_Reference).'@Descriptor',
+ RelationshipTypeID: LookUp(First(ThisRecord.Related_Person_Relationship_Reference).ID, '@type' = "Related_Person_Relationship_ID").'#text',
+ IsPrimary: ThisRecord.Emergency_Contact.Emergency_Contact_Data.'@Primary',
+ Priority: ThisRecord.Emergency_Contact.Emergency_Contact_Data.'@Priority',
+ WID: LookUp(ThisRecord.Emergency_Contact.Emergency_Contact_Reference.ID, '@type' = "WID").'#text'
+ }
+ )
+
+ - kind: ConditionGroup
+ id: check_contacts_exist
+ conditions:
+ - id: has_existing_contacts
+ condition: =CountRows(Topic.contactSelectionList) > 0
+ displayName: If user has existing emergency contacts
+ actions:
+ - kind: SetVariable
+ id: build_selection_choices
+ displayName: Build selection choices
+ variable: Topic.selectionChoices
+ value: |
+ =ForAll(
+ Filter(
+ SortByColumns(Topic.contactSelectionList, "IsPrimary", SortOrder.Descending, "Priority", SortOrder.Ascending),
+ !IsBlank(ThisRecord.DisplayName) && !IsBlank(ThisRecord.WID)
+ ),
+ {
+ title: If(IsBlank(ThisRecord.DisplayName), "Unknown Contact", ThisRecord.DisplayName),
+ value: ThisRecord.WID
+ }
+ )
+
+ - kind: SetVariable
+ id: set_workday_url_2
+ variable: Topic.WorkdayUrl
+ value: https://impl.workday.com//home.htmld
+
+ - kind: SetVariable
+ id: build_contact_table
+ displayName: Build formatted contact table
+ variable: Topic.contactTableText
+ value: |
+ ="| Name | Relationship | Phone | Address |" & Char(10) & "|------|-------------|-------|---------|" & Char(10) & Concat(
+ SortByColumns(Topic.contactSelectionList, "IsPrimary", SortOrder.Descending, "Priority", SortOrder.Ascending),
+ "| " & If(!IsBlank(ThisRecord.DisplayName), ThisRecord.DisplayName, "Unknown") & If(ThisRecord.IsPrimary = "true" || ThisRecord.IsPrimary = "1", " ★", "") & " | " & If(!IsBlank(ThisRecord.RelationshipType), ThisRecord.RelationshipType, "—") & " | " & If(!IsBlank(ThisRecord.Phone), ThisRecord.Phone, "—") & " | " & If(!IsBlank(ThisRecord.Address), ThisRecord.Address, "—") & " |",
+ Char(10)
+ )
+
+ # View-only mode: show table as Adaptive Card (full width) and end
+ - kind: ConditionGroup
+ id: check_view_only
+ conditions:
+ - id: is_view_only_mode
+ condition: =Topic.isViewOnly = true
+ displayName: User only wants to view contacts
+ actions:
+ - kind: SetVariable
+ id: set_view_intro_text
+ variable: Topic.viewIntroText
+ value: ="Here are your emergency contacts from [Workday](" & Topic.WorkdayUrl & "), an HR platform your company uses."
+
+ - kind: SendActivity
+ id: view_only_intro
+ activity: "{Topic.viewIntroText}"
+
+ - kind: SendActivity
+ id: view_only_table_card
+ activity:
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.3",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Your emergency contacts (" & Text(CountRows(Topic.contactSelectionList)) & ")",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true
+ },
+ {
+ type: "ColumnSet",
+ separator: true,
+ spacing: "Medium",
+ columns: [
+ { type: "Column", width: 2, items: [{ type: "TextBlock", text: "Name", weight: "Bolder", wrap: true }] },
+ { type: "Column", width: 2, items: [{ type: "TextBlock", text: "Relationship", weight: "Bolder", wrap: true }] },
+ { type: "Column", width: 2, items: [{ type: "TextBlock", text: "Phone", weight: "Bolder", wrap: true }] },
+ { type: "Column", width: 3, items: [{ type: "TextBlock", text: "Address", weight: "Bolder", wrap: true }] }
+ ]
+ },
+ {
+ type: "Container",
+ items: ForAll(
+ SortByColumns(Topic.contactSelectionList, "IsPrimary", SortOrder.Descending, "Priority", SortOrder.Ascending),
+ {
+ type: "ColumnSet",
+ separator: true,
+ columns: [
+ { type: "Column", width: 2, items: [{ type: "TextBlock", text: If(!IsBlank(ThisRecord.DisplayName), ThisRecord.DisplayName, "Unknown") & If(ThisRecord.IsPrimary = "true" || ThisRecord.IsPrimary = "1", " ★", ""), wrap: true }] },
+ { type: "Column", width: 2, items: [{ type: "TextBlock", text: If(!IsBlank(ThisRecord.RelationshipType), ThisRecord.RelationshipType, "—"), wrap: true }] },
+ { type: "Column", width: 2, items: [{ type: "TextBlock", text: If(!IsBlank(ThisRecord.Phone), ThisRecord.Phone, "—"), wrap: true }] },
+ { type: "Column", width: 3, items: [{ type: "TextBlock", text: If(!IsBlank(ThisRecord.Address), ThisRecord.Address, "—"), wrap: true }] }
+ ]
+ }
+ )
+ }
+ ]
+ }
+
+ - kind: SendActivity
+ id: view_only_footer
+ activity: "If you need to update or add any information for these contacts, just let me know which one you'd like to change or if you want to add a new contact."
+
+ - kind: CancelAllDialogs
+ id: end_view_only
+
+ - kind: SetVariable
+ id: set_intro_msg_text
+ variable: Topic.introMsgText
+ value: ="Sure, I can help you with that. Here's where you can update and add emergency contacts. I've identified " & Text(CountRows(Topic.selectionChoices)) & " emergency contact(s) of yours from [Workday](" & Topic.WorkdayUrl & "), an HR platform your company uses."
+
+ - kind: SendActivity
+ id: intro_selection_msg
+ activity: "{Topic.introMsgText}"
+
+ - kind: AdaptiveCardPrompt
+ id: select_contact_card
+ displayName: Select emergency contact to update
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: If(Topic.isDeleteIntent, "Select an emergency contact to delete", "Select an emergency contact to update, delete, or add a new contact."),
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "TextBlock",
+ text: If(Topic.isDeleteIntent, "You have " & CountRows(Topic.selectionChoices) & " emergency contact(s). Select the contact you want to delete.", "You have " & CountRows(Topic.selectionChoices) & " emergency contact(s). Select to update or add a new contact."),
+ wrap: true,
+ spacing: "Small"
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "selectedContact",
+ label: "Emergency contact",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select an emergency contact.",
+ choices: ForAll(Topic.selectionChoices, { title: ThisRecord.title, value: ThisRecord.value }),
+ value: First(Topic.selectionChoices).value
+ }
+ ],
+ actions: If(Topic.isDeleteIntent,
+ [
+ { type: "Action.Submit", title: "Delete contact", id: "DELETE", data: { actionSubmitId: "DELETE" } },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", data: { actionSubmitId: "Cancel" }, associatedInputs: "none" }
+ ],
+ [
+ { type: "Action.Submit", title: "Continue", id: "Continue", data: { actionSubmitId: "Continue" } },
+ { type: "Action.Submit", title: "Delete contact", id: "DELETE", data: { actionSubmitId: "DELETE" } },
+ { type: "Action.Submit", title: "Add an emergency contact", id: "ADD_NEW", data: { actionSubmitId: "ADD_NEW" }, associatedInputs: "none" }
+ ]
+ )
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.selectionActionId
+ selectedContact: Topic.selectedContactWID
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ selectedContact: String
+
+ - kind: ConditionGroup
+ id: handle_selection_cancel
+ conditions:
+ - id: selection_cancelled
+ condition: =Topic.selectionActionId = "Cancel"
+ actions:
+ - kind: SendActivity
+ id: selection_cancel_msg
+ activity: Your request has been cancelled. Is there anything else you need help with?
+
+ - kind: CancelAllDialogs
+ id: selection_cancel_all
+
+ - kind: ConditionGroup
+ id: set_mode
+ conditions:
+ - id: is_update_mode
+ condition: =Topic.selectionActionId = "Continue"
+ displayName: Update existing contact
+ actions:
+ - kind: SetVariable
+ id: set_update_mode
+ variable: Topic.isUpdateMode
+ value: =true
+
+ - kind: SetVariable
+ id: set_selected_contact_data
+ variable: Topic.selectedContactData
+ value: =LookUp(Topic.contactSelectionList, WID = Topic.selectedContactWID)
+
+ - kind: ConditionGroup
+ id: handle_selection_delete
+ conditions:
+ - id: selection_delete_requested
+ condition: =Topic.selectionActionId = "DELETE"
+ displayName: User wants to delete from selection card
+ actions:
+ - kind: SetVariable
+ id: set_selected_contact_for_delete
+ variable: Topic.selectedContactData
+ value: =LookUp(Topic.contactSelectionList, WID = Topic.selectedContactWID)
+
+ - kind: GotoAction
+ id: goto_delete_confirmation
+ actionId: handle_delete_action
+
+ elseActions:
+ - kind: SetVariable
+ id: set_add_mode
+ variable: Topic.isUpdateMode
+ value: =false
+
+ elseActions:
+ - kind: SetVariable
+ id: set_add_mode_no_contacts
+ variable: Topic.isUpdateMode
+ value: =false
+
+ - kind: SendActivity
+ id: no_contacts_msg
+ activity: You don't have any emergency contacts configured yet. Let's add one now.
+
+ # Show context message before form (only for update mode)
+ - kind: ConditionGroup
+ id: show_update_context_msg
+ conditions:
+ - id: is_update_show_msg
+ condition: =Topic.isUpdateMode = true
+ actions:
+ - kind: SetVariable
+ id: set_update_context_text
+ variable: Topic.updateContextText
+ value: ="You've pulled up " & Topic.selectedContactData.FirstName & " " & Topic.selectedContactData.LastName & "'s emergency contact details. In this form you can edit details or remove " & Topic.selectedContactData.FirstName & " from your emergency contacts."
+
+ - kind: SendActivity
+ id: update_context_msg
+ activity: "{Topic.updateContextText}"
+
+ # Step 1: Country selection card (needed to filter states in the next card)
+ - kind: AdaptiveCardPrompt
+ id: country_selection_card
+ displayName: Select country for address
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Select country",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "TextBlock",
+ text: "Choose the country for this contact's address. The state/province options in the next step will be filtered accordingly.",
+ wrap: true,
+ size: "Small",
+ spacing: "Small"
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "countryCode",
+ label: "Country",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a country.",
+ choices: [
+ { title: "United States", value: "USA" },
+ { title: "Canada", value: "CAN" },
+ { title: "United Kingdom", value: "GBR" },
+ { title: "India", value: "IND" },
+ { title: "Australia", value: "AUS" }
+ ],
+ value: If(Topic.isUpdateMode, Topic.selectedContactData.CountryCode, "USA")
+ }
+ ],
+ actions: [
+ { type: "Action.Submit", title: "Continue", id: "Continue", data: { actionSubmitId: "Continue" } },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", data: { actionSubmitId: "Cancel" }, associatedInputs: "none" }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.countrySelectionActionId
+ countryCode: Topic.countryCode
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ countryCode: String
+
+ - kind: ConditionGroup
+ id: handle_country_cancel
+ conditions:
+ - id: country_cancelled
+ condition: =Topic.countrySelectionActionId = "Cancel"
+ actions:
+ - kind: SendActivity
+ id: country_cancel_msg
+ activity: Your request has been cancelled. Is there anything else you need help with?
+
+ - kind: CancelAllDialogs
+ id: country_cancel_all
+
+ # Step 2: Build filtered state/province choices based on selected country
+ - kind: SetVariable
+ id: set_filtered_state_choices
+ displayName: Filter states by selected country
+ variable: Topic.filteredStateProvinceChoices
+ value: =Filter(Global.StateProvinceChoices, country = Topic.countryCode)
+
+ # Map address country to default phone dial code
+ - kind: SetVariable
+ id: set_default_phone_dial_code
+ displayName: Default phone dial code for selected country
+ variable: Topic.defaultPhoneDialCode
+ value: =Switch(Topic.countryCode, "USA", "1", "CAN", "1", "GBR", "44", "IND", "91", "AUS", "61", "1")
+
+ # Step 3: Main emergency contact form (with filtered states)
+ - kind: AdaptiveCardPrompt
+ id: emergency_contact_form
+ displayName: Emergency contact form
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: If(Topic.isUpdateMode, "Update emergency contact", "Add emergency contact"),
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "TextBlock",
+ text: "Fields marked with * are required.",
+ wrap: true,
+ size: "Small",
+ spacing: "Small"
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "firstName",
+ label: "First name",
+ placeholder: "Enter first name",
+ isRequired: true,
+ errorMessage: "First name is required.",
+ maxLength: 100,
+ value: If(Topic.isUpdateMode, Topic.selectedContactData.FirstName, "")
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "lastName",
+ label: "Last name",
+ placeholder: "Enter last name",
+ isRequired: true,
+ errorMessage: "Last name is required.",
+ maxLength: 100,
+ value: If(Topic.isUpdateMode, Topic.selectedContactData.LastName, "")
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "priority",
+ label: "Priority (only applies if not primary)",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a priority.",
+ choices: [
+ { title: "2 - High", value: "2" },
+ { title: "3", value: "3" },
+ { title: "4", value: "4" },
+ { title: "5 - Medium", value: "5" },
+ { title: "6", value: "6" },
+ { title: "7", value: "7" },
+ { title: "8", value: "8" },
+ { title: "9", value: "9" },
+ { title: "10 - Low", value: "10" }
+ ],
+ value: If(Topic.isUpdateMode, If(Value(Topic.selectedContactData.Priority) > 1, Topic.selectedContactData.Priority, "2"), "2")
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "relationshipType",
+ label: "Relationship",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a relationship type.",
+ choices: ForAll(Global.RelatedPersonRelationshipLookupTable, { title: ThisRecord.Referenced_Object_Descriptor, value: ThisRecord.ID }),
+ value: If(Topic.isUpdateMode, Topic.selectedContactData.RelationshipTypeID, First(Global.RelatedPersonRelationshipLookupTable).ID)
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "Input.Toggle",
+ id: "isPrimaryContact",
+ title: "Set as primary emergency contact",
+ value: If(Topic.isUpdateMode, If(Topic.selectedContactData.IsPrimary = "true" || Topic.selectedContactData.IsPrimary = "1", "true", "false"), "false"),
+ valueOn: "true",
+ valueOff: "false"
+ },
+ {
+ type: "Input.ChoiceSet",
+ id: "phoneDeviceType",
+ label: "Phone type",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a phone type.",
+ separator: true,
+ choices: [
+ { title: "Mobile", value: "Mobile" },
+ { title: "Home", value: "Home" },
+ { title: "Work", value: "Work" }
+ ],
+ value: "Mobile",
+ spacing: "Medium"
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "phoneCountryCode",
+ label: "Phone country code",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a country code.",
+ choices: ForAll(Global.CountryCodeLookupTable, { title: ThisRecord.Referenced_Object_Descriptor, value: ThisRecord.ID }),
+ value: If(Topic.isUpdateMode, LookUp(Global.CountryCodeLookupTable, Last(Split(ID, "_")).Value = Topic.selectedContactData.PhoneCountryCode).ID, LookUp(Global.CountryCodeLookupTable, Last(Split(ID, "_")).Value = Topic.defaultPhoneDialCode).ID)
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "phoneNumber",
+ label: "Phone number",
+ placeholder: "Enter phone number",
+ isRequired: true,
+ errorMessage: "Phone number is required.",
+ maxLength: 20,
+ value: If(Topic.isUpdateMode, Topic.selectedContactData.PhoneNumber, "")
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "Input.Text",
+ id: "addressLine1",
+ label: "Address line 1",
+ placeholder: "Enter street address",
+ isRequired: true,
+ errorMessage: "Address is required.",
+ maxLength: 200,
+ value: If(Topic.isUpdateMode, Topic.selectedContactData.AddressLine1, ""),
+ separator: true,
+ spacing: "Medium"
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "city",
+ label: "City",
+ placeholder: "Enter city",
+ isRequired: true,
+ errorMessage: "City is required.",
+ maxLength: 100,
+ value: If(Topic.isUpdateMode, Topic.selectedContactData.City, "")
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.ChoiceSet",
+ id: "stateProvince",
+ label: "State/Province",
+ style: "compact",
+ isRequired: true,
+ errorMessage: "Please select a state/province.",
+ choices: If(CountRows(Topic.filteredStateProvinceChoices) > 0, ForAll(Topic.filteredStateProvinceChoices, { title: ThisRecord.title, value: ThisRecord.value }), [{ title: "N/A — enter details in Address field", value: "N_A" }]),
+ value: If(Topic.isUpdateMode && !IsBlank(Topic.selectedContactData.StateProvince), Topic.selectedContactData.StateProvince, If(CountRows(Topic.filteredStateProvinceChoices) > 0, First(Topic.filteredStateProvinceChoices).value, "N_A"))
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "ColumnSet",
+ columns: [
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "Input.Text",
+ id: "postalCode",
+ label: "Postal code",
+ placeholder: "Enter postal code",
+ isRequired: true,
+ errorMessage: "Postal code is required.",
+ maxLength: 20,
+ value: If(Topic.isUpdateMode, Topic.selectedContactData.PostalCode, "")
+ }
+ ]
+ },
+ {
+ type: "Column",
+ width: "stretch",
+ items: [
+ {
+ type: "TextBlock",
+ text: "Country",
+ size: "Small",
+ weight: "Bolder",
+ spacing: "Small"
+ },
+ {
+ type: "TextBlock",
+ text: LookUp([{v:"USA",t:"United States"},{v:"CAN",t:"Canada"},{v:"GBR",t:"United Kingdom"},{v:"IND",t:"India"},{v:"AUS",t:"Australia"}], v = Topic.countryCode).t,
+ wrap: true,
+ spacing: "Small"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ actions: If(Topic.isUpdateMode,
+ [
+ { type: "Action.Submit", title: "Submit changes", id: "Submit", data: { actionSubmitId: "Submit" } },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", data: { actionSubmitId: "Cancel" }, associatedInputs: "none" }
+ ],
+ [
+ { type: "Action.Submit", title: "Submit", id: "Submit", data: { actionSubmitId: "Submit" } },
+ { type: "Action.Submit", title: "Cancel", id: "Cancel", data: { actionSubmitId: "Cancel" }, associatedInputs: "none" }
+ ]
+ )
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.formActionId
+ addressLine1: Topic.addressLine1
+ city: Topic.city
+ firstName: Topic.firstName
+ isPrimaryContact: Topic.isPrimaryContact
+ lastName: Topic.lastName
+ phoneCountryCode: Topic.phoneCountryCode
+ phoneDeviceType: Topic.phoneDeviceType
+ phoneNumber: Topic.phoneNumber
+ postalCode: Topic.postalCode
+ priority: Topic.priority
+ relationshipType: Topic.relationshipType
+ stateProvince: Topic.stateProvince
+
+ outputType:
+ properties:
+ actionSubmitId: String
+ addressLine1: String
+ city: String
+ firstName: String
+ isPrimaryContact: String
+ lastName: String
+ phoneCountryCode: String
+ phoneDeviceType: String
+ phoneNumber: String
+ postalCode: String
+ priority: String
+ relationshipType: String
+ stateProvince: String
+
+ - kind: ConditionGroup
+ id: handle_form_cancel
+ conditions:
+ - id: form_cancelled
+ condition: =Topic.formActionId = "Cancel"
+ actions:
+ - kind: SendActivity
+ id: form_cancel_msg
+ activity: Your request has been cancelled. Is there anything else you need help with?
+
+ - kind: CancelAllDialogs
+ id: form_cancel_all
+
+ # Handle delete action - show confirmation (reached from selection card or form)
+ - kind: ConditionGroup
+ id: handle_delete_action
+ conditions:
+ - id: delete_requested
+ condition: =Topic.formActionId = "Delete" || Topic.selectionActionId = "DELETE"
+ displayName: User wants to delete contact
+ actions:
+ # Check if contact is primary - don't allow delete
+ - kind: ConditionGroup
+ id: check_primary_delete
+ conditions:
+ - id: is_primary_contact
+ condition: =Topic.selectedContactData.IsPrimary = "true" || Topic.selectedContactData.IsPrimary = "1"
+ displayName: Cannot delete primary contact
+ actions:
+ - kind: SendActivity
+ id: cannot_delete_primary_msg
+ activity: You cannot delete your primary emergency contact. Please designate another contact as primary first, then you can remove this one.
+
+ - kind: CancelAllDialogs
+ id: cancel_primary_delete
+
+ elseActions:
+ # Not primary - proceed with delete confirmation
+ - kind: SetVariable
+ id: set_delete_confirm_text
+ variable: Topic.deleteConfirmText
+ value: ="Are you sure you want to delete this emergency contact?"
+
+ - kind: SendActivity
+ id: delete_confirm_text_msg
+ activity: "{Topic.deleteConfirmText}"
+
+ - kind: AdaptiveCardPrompt
+ id: confirm_delete_card
+ displayName: Confirm delete
+ card: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "Confirm deletion",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "FactSet",
+ spacing: "Medium",
+ facts: [
+ { title: "Name", value: Topic.selectedContactData.FirstName & " " & Topic.selectedContactData.LastName },
+ { title: "Priority", value: LookUp([{v:"2",t:"2 - High"},{v:"3",t:"3"},{v:"4",t:"4"},{v:"5",t:"5 - Medium"},{v:"6",t:"6"},{v:"7",t:"7"},{v:"8",t:"8"},{v:"9",t:"9"},{v:"10",t:"10 - Low"}], v = Topic.selectedContactData.Priority).t },
+ { title: "Relationship", value: Topic.selectedContactData.RelationshipType },
+ { title: "Phone", value: Topic.selectedContactData.Phone },
+ { title: "Address", value: Topic.selectedContactData.Address }
+ ]
+ }
+ ],
+ actions: [
+ { type: "Action.Submit", title: "Yes, delete", id: "Yes", data: { actionSubmitId: "Yes" } },
+ { type: "Action.Submit", title: "Cancel", id: "No", data: { actionSubmitId: "No" } }
+ ]
+ }
+ output:
+ binding:
+ actionSubmitId: Topic.confirmDeleteAction
+
+ outputType:
+ properties:
+ actionSubmitId: String
+
+ - kind: ConditionGroup
+ id: check_delete_confirmation
+ conditions:
+ - id: confirmed_delete
+ condition: =Topic.confirmDeleteAction = "Yes"
+ displayName: User confirmed delete
+ actions:
+ - kind: BeginDialog
+ id: execute_delete
+ displayName: Execute Workday Delete
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{Emergency_Contact_WID}"",""value"":""" & Topic.selectedContactWID & """},{""key"":""{Priority}"",""value"":""" & Topic.selectedContactData.Priority & """},{""key"":""{Relationship_Type}"",""value"":""" & Topic.selectedContactData.RelationshipTypeID & """},{""key"":""{Comment}"",""value"":""Removing emergency contact via Copilot""}]}"
+ scenarioName: msdyn_DeleteEmergencyContact
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ConditionGroup
+ id: report_delete_result
+ conditions:
+ - id: delete_failed
+ condition: =Topic.isSuccess = false
+ actions:
+ # Parse error message for display
+ - kind: SetVariable
+ id: set_delete_error_raw
+ variable: Topic.deleteErrorRaw
+ value: =Topic.errorResponse
+
+ - kind: SetVariable
+ id: parse_delete_error
+ variable: Topic.deleteErrorParsed
+ value: =IfError(Text(ParseJSON(Topic.deleteErrorRaw).error.message), IfError(Text(ParseJSON(Topic.deleteErrorRaw).message), Topic.deleteErrorRaw))
+
+ - kind: SetVariable
+ id: set_delete_error_display
+ variable: Topic.deleteErrorDisplay
+ value: =If(IsBlank(Topic.deleteErrorParsed), "An unknown error occurred.", Topic.deleteErrorParsed)
+
+ - kind: SendActivity
+ id: delete_failure_msg
+ activity: |-
+ An error occurred and the emergency contact was not deleted.
+
+ **Error details:** {Topic.deleteErrorDisplay}
+
+ Please try again or contact support.
+
+ - kind: CancelAllDialogs
+ id: end_on_delete_failure
+
+ elseActions:
+ - kind: SendActivity
+ id: delete_success_msg
+ activity:
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: "✅ Emergency contact deleted",
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "FactSet",
+ spacing: "Medium",
+ facts: [
+ { title: "Name", value: Topic.selectedContactData.FirstName & " " & Topic.selectedContactData.LastName },
+ { title: "Priority", value: LookUp([{v:"2",t:"2 - High"},{v:"3",t:"3"},{v:"4",t:"4"},{v:"5",t:"5 - Medium"},{v:"6",t:"6"},{v:"7",t:"7"},{v:"8",t:"8"},{v:"9",t:"9"},{v:"10",t:"10 - Low"}], v = Topic.selectedContactData.Priority).t },
+ { title: "Relationship", value: Topic.selectedContactData.RelationshipType },
+ { title: "Phone", value: Topic.selectedContactData.Phone },
+ { title: "Address", value: Topic.selectedContactData.Address }
+ ]
+ }
+ ]
+ }
+
+ - kind: CancelAllDialogs
+ id: end_delete_dialogs
+
+ elseActions:
+ - kind: SendActivity
+ id: delete_cancelled_msg
+ activity: Delete cancelled. Is there anything else I can help you with?
+
+ - kind: CancelAllDialogs
+ id: cancel_delete_dialogs
+
+ # Only process submit action (not cancel or delete)
+ - kind: ConditionGroup
+ id: check_submit_action
+ conditions:
+ - id: is_submit_action
+ condition: =Topic.formActionId = "Submit"
+ displayName: User submitted the form
+ actions:
+ - kind: ConditionGroup
+ id: submit_to_workday
+ conditions:
+ - id: is_update_submit
+ condition: =Topic.isUpdateMode = true
+ displayName: Submit update to Workday
+ actions:
+ - kind: BeginDialog
+ id: execute_update
+ displayName: Execute Workday Update
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{Emergency_Contact_WID}"",""value"":""" & Topic.selectedContactWID & """},{""key"":""{Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""},{""key"":""{First_Name}"",""value"":""" & Topic.firstName & """},{""key"":""{Last_Name}"",""value"":""" & Topic.lastName & """},{""key"":""{Primary}"",""value"":""" & Topic.isPrimaryContact & """},{""key"":""{Priority}"",""value"":""" & If(Topic.isPrimaryContact = "true", "1", Topic.priority) & """},{""key"":""{Relationship_Type}"",""value"":""" & Topic.relationshipType & """},{""key"":""{Phone_Number}"",""value"":""" & Topic.phoneNumber & """},{""key"":""{Phone_Device_Type}"",""value"":""" & Topic.phoneDeviceType & """},{""key"":""{Phone_Country_Code}"",""value"":""" & Last(Split(Topic.phoneCountryCode, "_")).Value & """},{""key"":""{Address_Line_1}"",""value"":""" & Topic.addressLine1 & """},{""key"":""{City}"",""value"":""" & Topic.city & """},{""key"":""{State_Province}"",""value"":""" & Topic.stateProvince & """},{""key"":""{Postal_Code}"",""value"":""" & Topic.postalCode & """},{""key"":""{Country_Code}"",""value"":""" & Topic.countryCode & """},{""key"":""{Address_Country_Code}"",""value"":""" & Topic.countryCode & """},{""key"":""{Comment}"",""value"":""Updating emergency contact""}]}"
+ scenarioName: msdyn_UpdateEmergencyContact
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ elseActions:
+ - kind: BeginDialog
+ id: execute_add
+ displayName: Execute Workday Add
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Employee_ID}"",""value"":""" & Global.ESS_UserContext_Employee_Id & """},{""key"":""{Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""},{""key"":""{First_Name}"",""value"":""" & Topic.firstName & """},{""key"":""{Last_Name}"",""value"":""" & Topic.lastName & """},{""key"":""{Primary}"",""value"":""" & Topic.isPrimaryContact & """},{""key"":""{Priority}"",""value"":""" & If(Topic.isPrimaryContact = "true", "1", Topic.priority) & """},{""key"":""{Relationship_Type}"",""value"":""" & Topic.relationshipType & """},{""key"":""{Phone_Number}"",""value"":""" & Topic.phoneNumber & """},{""key"":""{Phone_Device_Type}"",""value"":""" & Topic.phoneDeviceType & """},{""key"":""{Phone_Country_Code}"",""value"":""" & Last(Split(Topic.phoneCountryCode, "_")).Value & """},{""key"":""{Address_Line_1}"",""value"":""" & Topic.addressLine1 & """},{""key"":""{City}"",""value"":""" & Topic.city & """},{""key"":""{State_Province}"",""value"":""" & Topic.stateProvince & """},{""key"":""{Postal_Code}"",""value"":""" & Topic.postalCode & """},{""key"":""{Country_Code}"",""value"":""" & Topic.countryCode & """},{""key"":""{Address_Country_Code}"",""value"":""" & Topic.countryCode & """},{""key"":""{Comment}"",""value"":""Adding emergency contact""}]}"
+ scenarioName: msdyn_HRWorkdayHCMEmployeeAddEmergencyContact
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ConditionGroup
+ id: report_result
+ conditions:
+ - id: failed
+ condition: =Topic.isSuccess = false
+ actions:
+ # Parse error message for display
+ - kind: SetVariable
+ id: set_submit_error_raw
+ variable: Topic.submitErrorRaw
+ value: =Topic.errorResponse
+
+ - kind: SetVariable
+ id: parse_submit_error
+ variable: Topic.submitErrorParsed
+ value: =IfError(Text(ParseJSON(Topic.submitErrorRaw).error.message), IfError(Text(ParseJSON(Topic.submitErrorRaw).message), Topic.submitErrorRaw))
+
+ - kind: SetVariable
+ id: set_submit_error_display
+ variable: Topic.submitErrorDisplay
+ value: =If(IsBlank(Topic.submitErrorParsed), "An unknown error occurred.", Topic.submitErrorParsed)
+
+ - kind: SetVariable
+ id: set_failure_msg_text
+ variable: Topic.failureMsgText
+ value: =If(Topic.isUpdateMode, "An error occurred and your emergency contact was not updated.", "An error occurred and your emergency contact was not added.")
+
+ - kind: SendActivity
+ id: failure_msg
+ activity: |-
+ {Topic.failureMsgText}
+
+ **Error details:** {Topic.submitErrorDisplay}
+
+ Please try again or contact support.
+
+ - kind: CancelAllDialogs
+ id: end_on_failure
+
+ elseActions:
+ - kind: SetVariable
+ id: set_workday_url
+ variable: Topic.WorkdayUrl
+ value: ="https://impl.workday.com//home.htmld"
+
+ - kind: SetVariable
+ id: set_success_intro_text
+ variable: Topic.successIntroText
+ value: =If(Topic.isUpdateMode, "Great! You've updated your emergency contact.", "Great! You've added a new emergency contact.")
+
+ - kind: SendActivity
+ id: success_intro_msg
+ activity: "{Topic.successIntroText}"
+
+ - kind: SendActivity
+ id: success_card
+ activity:
+ attachments:
+ - kind: AdaptiveCardTemplate
+ cardContent: |-
+ ={
+ type: "AdaptiveCard",
+ '$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
+ version: "1.5",
+ body: [
+ {
+ type: "TextBlock",
+ text: If(Topic.isUpdateMode, "✅ Emergency contact updated", "✅ Emergency contact added"),
+ weight: "Bolder",
+ size: "Medium",
+ wrap: true,
+ spacing: "Medium"
+ },
+ {
+ type: "FactSet",
+ spacing: "Medium",
+ facts: [
+ { title: "Name", value: Topic.firstName & " " & Topic.lastName },
+ { title: "Priority", value: If(Topic.isPrimaryContact = "true", "Primary", LookUp([{v:"2",t:"2 - High"},{v:"3",t:"3"},{v:"4",t:"4"},{v:"5",t:"5 - Medium"},{v:"6",t:"6"},{v:"7",t:"7"},{v:"8",t:"8"},{v:"9",t:"9"},{v:"10",t:"10 - Low"}], v = Topic.priority).t) },
+ { title: "Relationship", value: LookUp(Global.RelatedPersonRelationshipLookupTable, ID = Topic.relationshipType).Referenced_Object_Descriptor },
+ { title: "Phone", value: "+" & Last(Split(Topic.phoneCountryCode, "_")).Value & "-" & Topic.phoneNumber & " (" & Topic.phoneDeviceType & ")" },
+ { title: "Address", value: Topic.addressLine1 & " " & Topic.city & ", " & Topic.countryCode & " " & Topic.postalCode }
+ ]
+ }
+ ]
+ }
+
+ - kind: SendActivity
+ id: success_footer_msg
+ activity: Is there anything else I can help you with?
+
+ - kind: CancelAllDialogs
+ id: end_dialogs
+
+ elseActions:
+ # This handles case when formActionId is not "Submit" (fallback for Cancel/Delete that weren't caught)
+ - kind: CancelAllDialogs
+ id: end_unexpected_action
+
+inputType:
+ properties:
+ InputAction:
+ displayName: InputAction
+ description: The action the user wants to perform (add, update, manage).
+ type: String
+
+outputType: {}
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/update_emergency_contact.png b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/update_emergency_contact.png
new file mode 100644
index 00000000..a91628fa
Binary files /dev/null and b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/update_emergency_contact.png differ
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/update_emergency_contact_2.png b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/update_emergency_contact_2.png
new file mode 100644
index 00000000..46ae5a9d
Binary files /dev/null and b/EmployeeSelfServiceAgent/WorkdayDA/EmployeeScenarios/WorkdayManageEmergencyContact/update_emergency_contact_2.png differ
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/README.md b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/README.md
new file mode 100644
index 00000000..89ed91da
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/README.md
@@ -0,0 +1,11 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Manager Scenarios
+
+Copilot Studio topics for manager self-service actions in Workday.
+
+| Folder | Description |
+| --- | --- |
+| [WorkdayGetManagerReporteesTimeInPosition/](./WorkdayGetManagerReporteesTimeInPosition/) | View reportees' time in position |
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/README.md b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/README.md
new file mode 100644
index 00000000..da976131
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/README.md
@@ -0,0 +1,110 @@
+---
+nav_exclude: true
+search_exclude: false
+---
+# Workday Get Manager Reportees Time In Position
+
+## Overview
+This scenario enables managers to view their direct reports along with how long each employee has been in their current position. It retrieves team member information from Workday HCM and calculates the time in position for each reportee.
+
+## Files
+
+| File | Description |
+|------|-------------|
+| `topic.yaml` | Copilot Studio topic definition with trigger phrases and Power Fx logic |
+| `msdyn_GetManagerReportees.xml` | XML template for Workday Get_Workers API call |
+
+## Prerequisites
+
+### Global Variables Required
+- `Global.ESS_UserContext_ManagerOrganizationId` - The manager's organization ID in Workday (used to filter direct reports)
+
+### Workday API
+- **Service**: Human_Resources
+- **Operation**: Get_Workers
+- **Version**: v42.0
+
+## Scenario Details
+
+### What It Does
+1. Retrieves all employees in the manager's organization (direct reports)
+2. Extracts key employee information (Name, Title, Position Start Date, etc.)
+3. Calculates Time in Position for each employee using Power Fx DateDiff functions
+4. Presents results with tenure information formatted as "X years, Y months, Z days"
+
+### Data Retrieved
+| Field | Description |
+|-------|-------------|
+| EmployeeID | Workday Employee ID |
+| Name | Employee's legal formatted name |
+| BusinessTitle | Current job title |
+| WorkerType | Employee or Contingent Worker |
+| JobProfile | Job profile name |
+| Location | Business site/location name |
+| PositionStartDate | Date employee started current position |
+| HireDate | Original hire date |
+| Status | Active/Inactive status |
+| TimeInPositionYears | Calculated years in current position |
+| TimeInPositionMonths | Calculated remaining months |
+| TimeInPositionDays | Calculated remaining days |
+| TimeInPosition | Formatted string (e.g., "2 years, 3 months, 15 days") |
+
+### Trigger Phrases
+- "Show me my team's time in position"
+- "How long have my direct reports been in their roles"
+- "Get reportees time in current position"
+- "List my team members with their position tenure"
+- "Who on my team has been in their role the longest"
+- "Show tenure for my direct reports"
+- "My reportees job tenure"
+- "Time in position for my team"
+
+## Time In Position Calculation
+
+The scenario uses Power Fx formulas to calculate time in position:
+
+```
+TimeInPositionYears = Int(DateDiff(DateValue(PositionStartDate), Now(), TimeUnit.Months) / 12)
+TimeInPositionMonths = Mod(DateDiff(DateValue(PositionStartDate), Now(), TimeUnit.Months), 12)
+TimeInPositionDays = DateDiff(DateAdd(DateAdd(DateValue(PositionStartDate), Years, TimeUnit.Years), Months, TimeUnit.Months), Now(), TimeUnit.Days)
+```
+
+## Response Group Configuration
+
+The XML template is optimized for performance by:
+- **Including**: Reference, Personal Information, Employment Information
+- **Excluding**: Compensation, Benefits, Documents, Photos, and other unnecessary data
+- **Filtering**: Only active workers (`Exclude_Inactive_Workers: true`)
+
+## Setup Instructions
+
+1. **Import the Topic**: Import `topic.yaml` into your Copilot Studio agent
+2. **Add XML Template**: Upload `msdyn_GetManagerReportees.xml` to your Workday connector configuration
+3. **Configure Connection**: Ensure your Workday connector connection reference is properly set in the topic
+4. **Set Global Variable**: Make sure `Global.ESS_UserContext_ManagerOrganizationId` is populated (typically from user authentication context)
+
+## Model Instructions
+
+The topic includes model instructions for the AI to:
+- Display results as a nested markdown list
+- Format Time in Position clearly
+- Show Name, Job Title, Time in Position, Start Date, and Status for each reportee
+- Sort or group results based on user's question context
+
+## Example Output
+
+When a manager asks "Show me my team's time in position", the agent displays:
+
+```
+Here are your direct reports and their time in current position:
+
+- **John Smith** - Senior Developer
+ - Time in Position: 2 years, 5 months, 12 days
+ - Position Start: 2022-07-15
+ - Status: Active
+
+- **Jane Doe** - Product Manager
+ - Time in Position: 1 year, 2 months, 3 days
+ - Position Start: 2023-10-20
+ - Status: Active
+```
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/msdyn_GetManagerReportees.xml b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/msdyn_GetManagerReportees.xml
new file mode 100644
index 00000000..72a67a03
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/msdyn_GetManagerReportees.xml
@@ -0,0 +1,124 @@
+
+
+
+
+ User
+
+ Template_GetManagerReporteesRequest
+ Human_Resources
+ v42.0
+
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Worker_ID']/text()
+ EmployeeID
+
+
+ //*[local-name()='Worker_Descriptor']/text()
+ Name
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Job_Data']/*[local-name()='Position_Data']/*[local-name()='Business_Title']/text()
+ BusinessTitle
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Job_Data']/*[local-name()='Position_Data']/*[local-name()='Worker_Type_Reference']/@*[local-name()='Descriptor']
+ WorkerType
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Job_Data']/*[local-name()='Position_Data']/*[local-name()='Job_Profile_Summary_Data']/*[local-name()='Job_Profile_Reference']/@*[local-name()='Descriptor']
+ JobProfile
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Job_Data']/*[local-name()='Position_Data']/*[local-name()='Business_Site_Summary_Data']/*[local-name()='Location_Reference']/@*[local-name()='Descriptor']
+ Location
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Job_Data']/*[local-name()='Position_Data']/*[local-name()='Start_Date']/text()
+ PositionStartDate
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Status_Data']/*[local-name()='Hire_Date']/text()
+ HireDate
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Status_Data']/*[local-name()='Active']/text()
+ Status
+
+
+
+
+
+
+
+
+
+
+ {Manager_Org_ID}
+
+
+
+ true
+ false
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/topic.yaml b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/topic.yaml
new file mode 100644
index 00000000..c95b23e0
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayGetManagerReporteesTimeInPosition/topic.yaml
@@ -0,0 +1,188 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ You will respond to requests about time in position for direct reports of the user making the request.
+
+ For info on single direct report's time in position:
+ Heading - "[Report]'s time in position"
+ Verbatim line after heading: "[Report] has been in their current role as a [Position] for [LengthOfService]. I pulled this from Workday, an HR platform your company uses."
+ Sections as bold headers and non-bold bullets under: More about [Direct Report]'s position
+
+ For info on all direct reports time in position: Display in Table
+ Heading - "Team roles and time in position"
+ Verbatim line after heading: "Here's a list of your team members and how long they've been in their positions. I pulled this from Workday, an HR platform your company uses."
+ Table columns: Name, Title, Time in Position
+
+ Common Rules ALWAYS FOLLOW:
+ - ALWAYS add large sized heading with sentence case
+ - Introduction (Relevant) RIGHT AFTER HEADING & ADD SECTION LINE AFTER IT,
+ - Double check if rules are followed
+ Invalid: non-direct reports.
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent: {}
+ actions:
+ - kind: BeginDialog
+ id: jJpz3n
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{Manager_Org_ID}"",""value"":""" & Global.ESS_UserContext_ManagerOrganizationId & """}]}"
+ scenarioName: msdyn_GetManagerReportees
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: aXVFNu
+ displayName: Parse value to a record
+ variable: Topic.workdayResponseRecord
+ valueType:
+ kind: Record
+ properties:
+ BusinessTitle:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ EmployeeID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ HireDate:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ JobProfile:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ Location:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ Name:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ PositionStartDate:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ Status:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ WorkerType:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: setVariable_ztXmui
+ displayName: Extract data to table
+ variable: Topic.workdayResponseTable
+ value: |-
+ =ForAll(
+ Sequence(CountRows(Topic.workdayResponseRecord.EmployeeID)),
+ {
+ EmployeeID: Last(FirstN(Topic.workdayResponseRecord.EmployeeID, Value)).Value,
+ Name: Last(FirstN(Topic.workdayResponseRecord.Name, Value)).Value,
+ BusinessTitle: Last(FirstN(Topic.workdayResponseRecord.BusinessTitle, Value)).Value,
+ WorkerType: Last(FirstN(Topic.workdayResponseRecord.WorkerType, Value)).Value,
+ JobProfile: Last(FirstN(Topic.workdayResponseRecord.JobProfile, Value)).Value,
+ Location: Last(FirstN(Topic.workdayResponseRecord.Location, Value)).Value,
+ PositionStartDate: Last(FirstN(Topic.workdayResponseRecord.PositionStartDate, Value)).Value,
+ HireDate: Last(FirstN(Topic.workdayResponseRecord.HireDate, Value)).Value,
+ Status: If(Last(FirstN(Topic.workdayResponseRecord.Status, Value)).Value = "1", "Active", "Inactive")
+ }
+ )
+
+ - kind: SetVariable
+ id: setVariable_AddTimeInPosition
+ displayName: Add Time in Position calculations
+ variable: Topic.workdayResponseTableWithTimeInPosition
+ value: |-
+ =ForAll(
+ Topic.workdayResponseTable,
+ With(
+ {
+ positionDate: DateValue(PositionStartDate),
+ yearsCalc: RoundDown(DateDiff(DateValue(PositionStartDate), Today(), TimeUnit.Months) / 12, 0),
+ monthsCalc: Mod(DateDiff(DateValue(PositionStartDate), Today(), TimeUnit.Months), 12),
+ daysCalc: DateDiff(
+ DateAdd(DateValue(PositionStartDate), DateDiff(DateValue(PositionStartDate), Today(), TimeUnit.Months), TimeUnit.Months),
+ Today(),
+ TimeUnit.Days
+ )
+ },
+ {
+ EmployeeID: EmployeeID,
+ Name: Name,
+ BusinessTitle: BusinessTitle,
+ WorkerType: WorkerType,
+ JobProfile: JobProfile,
+ Location: Location,
+ PositionStartDate: PositionStartDate,
+ HireDate: HireDate,
+ Status: Status,
+ TimeInPositionYears: yearsCalc,
+ TimeInPositionMonths: monthsCalc,
+ TimeInPositionDays: daysCalc,
+ TimeInPosition:
+ If(yearsCalc > 0, yearsCalc & " year" & If(yearsCalc > 1, "s", "") & " ", "") &
+ If(monthsCalc > 0, monthsCalc & " month" & If(monthsCalc > 1, "s", "") & " ", "") &
+ daysCalc & " day" & If(daysCalc <> 1, "s", "")
+ }
+ )
+ )
+
+ - kind: SendActivity
+ id: sendActivity_bDM9UT
+ activity: |-
+ Here is your team's time in position information:
+ {Topic.workdayResponseTableWithTimeInPosition}
+
+inputType: {}
+outputType:
+ properties:
+ workdayResponseTableWithTimeInPosition:
+ displayName: workdayResponseTableWithTimeInPosition
+ type:
+ kind: Table
+ properties:
+ BusinessTitle: String
+ EmployeeID: String
+ HireDate: String
+ JobProfile: String
+ Location: String
+ Name: String
+ PositionStartDate: String
+ Status: String
+ TimeInPosition: String
+ TimeInPositionDays: Number
+ TimeInPositionMonths: Number
+ TimeInPositionYears: Number
+ WorkerType: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagerServiceAnniversary/msdyn_HRWorkdayHCMManagerServiceAnniversary.xml b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagerServiceAnniversary/msdyn_HRWorkdayHCMManagerServiceAnniversary.xml
new file mode 100644
index 00000000..54aa264c
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagerServiceAnniversary/msdyn_HRWorkdayHCMManagerServiceAnniversary.xml
@@ -0,0 +1,80 @@
+
+
+
+
+ User
+
+ msdyn_HRWorkdayHCMManagerServiceAnniversary_GetWorkersManagerRequest
+ Human_Resources
+ v41.0
+
+
+
+ Include_Employment_Information
+ true
+
+
+ Include_Personal_Information
+ true
+
+
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Worker_ID']/text()
+ WorkerID
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/*[local-name()='First_Name']/text()
+
+ FirstName
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/*[local-name()='Last_Name']/text()
+ LastName
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Status_Data']/*[local-name()='Hire_Date']/text()
+ HireDate
+
+
+
+
+
+
+
+
+
+
+ {ManagerOrgId}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagerServiceAnniversary/topic.yaml b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagerServiceAnniversary/topic.yaml
new file mode 100644
index 00000000..2002ee89
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagerServiceAnniversary/topic.yaml
@@ -0,0 +1,237 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ Use this topic when a MANAGER asks about the SERVICE ANNIVERSARY / work anniversary / employment anniversary / tenure milestone / hire-date anniversary of THEIR DIRECT REPORTS in Workday. ONLY for direct reports — NOT for self, manager, peers, spouse, sibling.
+
+ Triggers:
+ - "Service anniversary of my direct reports"
+ - "Upcoming work anniversaries on my team"
+ - "Service anniversaries of my reports next month"
+ - "Which of my reports has an anniversary coming up?"
+ - "Team tenure milestones"
+
+ Filter:
+ - "next month" → use isAnniversaryNextMonth
+ - Default: future anniversaries only, unless otherwise specified
+
+ Invalid: non-direct-reports. Self-anniversary belongs to the ServiceAnniversary topic.
+
+ Output rules:
+ - Output MUST be a markdown table based ONLY on {Topic.response}
+ - Columns: Employee Name, Hire Date, Upcoming Service Anniversary Date, Upcoming Milestone
+ - Do NOT return data if you don't have enough info
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: EmployeeName
+ description: Employee name whose service anniversary needs to be retrieved
+ entity: PersonNamePrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+ - kind: AutomaticTaskInput
+ propertyName: duration
+ description: Duration used to calculate nth service anniversary date
+ entity: NumberPrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - When are the service anniversaries of all my directs?
+ - What are the service anniversaries of my entire team?
+ - Show me the service anniversaries of my reports?
+ - What are the upcoming service anniversaries of my team?
+ - Any upcoming service anniversaries of my reports?
+ - Show me service anniversaries of my directs?
+ - What is [EmployeeName]'s next service anniversary?
+ - When is [EmployeeName]'s next year service anniversary?
+
+ actions:
+ - kind: BeginDialog
+ id: WmAHrc
+ displayName: Check manager criteria
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdayManagerCheck
+
+ - kind: SetVariable
+ id: setVariable_FrkyzI
+ displayName: Set current date
+ variable: Topic.currentDate
+ value: =Now()
+
+ - kind: BeginDialog
+ id: HZDypL
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{ManagerOrgId}"",""value"":""" & Global.ESS_UserContext_ManagerOrganizationId & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMManagerServiceAnniversary
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: Nh88X0
+ displayName: Parse Workday response
+ variable: Topic.parsedWorkdayResponse
+ valueType:
+ kind: Record
+ properties:
+ FirstName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ HireDate:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ LastName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ WorkerID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: setVariable_eJUuES
+ displayName: Build Workday response table
+ variable: Topic.workdayResponseTable
+ value: |-
+ =ForAll(
+ Sequence(CountRows(Topic.parsedWorkdayResponse.WorkerID)),
+ {
+ FirstName: Last(FirstN(Topic.parsedWorkdayResponse.FirstName, Value)).Value,
+ LastName: Last(FirstN(Topic.parsedWorkdayResponse.LastName, Value)).Value,
+ HireDate: Last(FirstN(Topic.parsedWorkdayResponse.HireDate, Value)).Value
+ }
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_P6QlNE
+ conditions:
+ - id: conditionItem_3RSvtM
+ condition: =!IsBlank(Topic.EmployeeName)
+ displayName: Check if EmployeeName provided
+ actions:
+ - kind: SetVariable
+ id: setVariable_yRTs4g
+ displayName: Filter for the given EmployeeName
+ variable: Topic.workdayResponseTable
+ value: |-
+ =Filter(
+ Topic.workdayResponseTable,
+ FirstName = Topic.EmployeeName Or
+ LastName = Topic.EmployeeName Or
+ Concatenate(FirstName, " ", LastName) = Topic.EmployeeName Or
+ Concatenate(LastName, " ", FirstName) = Topic.EmployeeName
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_5aSdys
+ conditions:
+ - id: conditionItem_U6JgqP
+ condition: =IsEmpty(Topic.workdayResponseTable)
+ displayName: Check is filtered data empty
+ actions:
+ - kind: SendActivity
+ id: sendActivity_ToPxHZ
+ displayName: Respond with no access message
+ activity: It looks like you don't have access to this information. Try making a new request.
+
+ - kind: SetVariable
+ id: setVariable_bqlpWp
+ displayName: Clear response table
+ variable: Topic.workdayResponseTable
+ value: =[]
+
+ - kind: CancelAllDialogs
+ id: PsP6gr
+
+ - kind: SetVariable
+ id: setVariable_CxBy4Y
+ displayName: Set response to formatted Workday response
+ variable: Topic.response
+ value: |-
+ =ForAll(Topic.workdayResponseTable, {
+ EmployeeName: 'FirstName' & " " & 'LastName',
+ HireDate: DateValue('HireDate'),
+ UpcomingServiceAnniversaryDate: If(
+ Today() <= DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years), TimeUnit.Years),
+ DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years), TimeUnit.Years),
+ DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years) + 1, TimeUnit.Years)
+ ),
+ UpcomingMilestone: DateDiff(DateValue('HireDate'), If(
+ Today() <= DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years), TimeUnit.Years),
+ DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years), TimeUnit.Years),
+ DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years) + 1, TimeUnit.Years)
+ ), TimeUnit.Years),
+ AnniversaryMonth: Month(If(
+ Today() <= DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years), TimeUnit.Years),
+ DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years), TimeUnit.Years),
+ DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years) + 1, TimeUnit.Years)
+ )),
+ isAnniversaryNextMonth: Month(If(
+ Today() <= DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years), TimeUnit.Years),
+ DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years), TimeUnit.Years),
+ DateAdd(DateValue('HireDate'), DateDiff(DateValue('HireDate'), Today(), TimeUnit.Years) + 1, TimeUnit.Years)
+ )) = Month(Now()) + 1
+ })
+
+ - kind: SetVariable
+ id: setVariable_AnybBj
+ displayName: Clear workday response
+ variable: Topic.workdayResponse
+ value: "\"\""
+
+ - kind: SendActivity
+ id: sendActivity_gO8pkz
+ activity: "{Topic.workdayResponseTable}"
+
+ - kind: EndDialog
+ id: Y2VqKn
+
+inputType:
+ properties:
+ duration:
+ displayName: duration
+ description: Duration used to calculate nth service anniversary date
+ type: Number
+
+ EmployeeName:
+ displayName: EmployeeName
+ description: Employee name whose service anniversary needs to be retrieved
+ type: String
+
+outputType:
+ properties:
+ response:
+ displayName: response
+ type:
+ kind: Table
+ properties:
+ AnniversaryMonth: Number
+ EmployeeName: String
+ HireDate: Date
+ isAnniversaryNextMonth: Boolean
+ UpcomingMilestone: Number
+ UpcomingServiceAnniversaryDate: Date
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-CompanyCode/msdyn_HRWorkdayHCMManagerDirectCompanyCode.xml b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-CompanyCode/msdyn_HRWorkdayHCMManagerDirectCompanyCode.xml
new file mode 100644
index 00000000..4773a2e3
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-CompanyCode/msdyn_HRWorkdayHCMManagerDirectCompanyCode.xml
@@ -0,0 +1,84 @@
+
+
+
+
+ User
+
+ Template_GetWorkersManagerRequest
+ Human_Resources
+ v41.0
+
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Worker_ID']/text()
+ WorkerID
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/*[local-name()='First_Name']/text()
+
+ FirstName
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/*[local-name()='Last_Name']/text()
+ LastName
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Organization_Data']/*[local-name()='Worker_Organization_Data'][*[local-name()='Organization_Data']/*[local-name()='Organization_Subtype_Reference']/*[local-name()='ID'][@*[local-name()='type']='Organization_Subtype_ID' and text()='Company']]/*[local-name()='Organization_Data']/*[local-name()='Organization_Code']/text()
+ CompanyCode
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Organization_Data']/*[local-name()='Worker_Organization_Data'][*[local-name()='Organization_Data']/*[local-name()='Organization_Subtype_Reference']/*[local-name()='ID'][@*[local-name()='type']='Organization_Subtype_ID' and text()='Company']]/*[local-name()='Organization_Data']/*[local-name()='Organization_Name']/text()
+ CompanyName
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Job_Data']/*[local-name()='Position_Data']/*[local-name()='Position_ID']/text()
+ PositionID
+
+
+
+
+
+
+
+
+
+
+ {ManagerOrgId}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+ false
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-CompanyCode/topic.yaml b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-CompanyCode/topic.yaml
new file mode 100644
index 00000000..368a7f95
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-CompanyCode/topic.yaml
@@ -0,0 +1,176 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ Use this topic when a MANAGER asks about the COMPANY CODE / company name / legal entity / employing entity of THEIR DIRECT REPORTS in Workday. ONLY for direct reports (people who report to the user) — NOT for self, manager, peers, spouse, sibling, or any non-direct-report.
+
+ Triggers:
+ - "What is the company code of my direct reports?"
+ - "What's the company / legal entity for my reports?"
+ - "Show the employing company of my team"
+ - "Direct reports' company code"
+ - "My reportees' company / legal entity"
+ - "Which company is each of my reports employed by?"
+
+ do NOT trigger for self-company-code questions belong to the WorkdayCompanyCode topic.
+
+ Output rules:
+ - Output MUST be a nested list in markdown
+ - Use ONLY data from {Topic.workdayResponseTable}
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: EmployeeName
+ description: Employee name whose company code needs to be fetched
+ entity: PersonNamePrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - Show me my team’s Company codes?
+ - What are the company codes for my reports?
+ - What company codes are mapped to my team members?
+
+ actions:
+ - kind: BeginDialog
+ id: dAhS4T
+ displayName: Check manager status
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdayManagerCheck
+
+ - kind: BeginDialog
+ id: Gt044B
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{ManagerOrgId}"",""value"":""" & Global.ESS_UserContext_ManagerOrganizationId & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMManagerDirectCompanyCode
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: aIxWzK
+ displayName: Parse value to a table
+ variable: Topic.WorkdayResponseRecord
+ valueType:
+ kind: Record
+ properties:
+ CompanyCode:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ CompanyName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ FirstName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ LastName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ WorkerID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: HC2h7w
+ displayName: Merge all records in table
+ variable: Topic.workdayResponseTable
+ value: |-
+ =ForAll(
+ Sequence(CountRows(Topic.WorkdayResponseRecord.WorkerID)),
+ {
+ FirstName: Last(FirstN(Topic.WorkdayResponseRecord.FirstName, Value)).Value,
+ LastName: Last(FirstN(Topic.WorkdayResponseRecord.LastName, Value)).Value,
+ CompanyCode: Last(FirstN(Topic.WorkdayResponseRecord.CompanyCode, Value)).Value,
+ CompanyName: Last(FirstN(Topic.WorkdayResponseRecord.CompanyName, Value)).Value
+ }
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_yepfHF
+ conditions:
+ - id: conditionItem_86c1ij
+ condition: =!IsBlank(Topic.EmployeeName)
+ displayName: Check if EmployeeName provided
+ actions:
+ - kind: SetVariable
+ id: setVariable_z1fKB1
+ displayName: Filter for the given EmployeeName
+ variable: Topic.workdayResponseTable
+ value: |
+ =Filter(
+ Topic.workdayResponseTable,
+ FirstName = Topic.EmployeeName Or
+ LastName = Topic.EmployeeName Or
+ Concatenate(FirstName, " ", LastName) = Topic.EmployeeName Or
+ Concatenate(LastName, " ", FirstName) = Topic.EmployeeName
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_BVYTlV
+ conditions:
+ - id: conditionItem_QmVzq2
+ condition: =IsEmpty(Topic.workdayResponseTable)
+ displayName: Check is filtered data empty
+ actions:
+ - kind: SendActivity
+ id: SN9frD
+ displayName: Respond with no access message
+ activity: It looks like you don't have access to this information. Try making a new request.
+
+ - kind: SetVariable
+ id: setVariable_wd0UBE
+ displayName: Clear workday response
+ variable: Topic.workdayResponse
+ value: =""
+
+ - kind: EndDialog
+ id: xVALae
+
+ - kind: SendActivity
+ id: sendActivity_Yqhll9
+ activity: |-
+ Here is your team's company code information:
+ {Topic.workdayResponseTable}
+
+inputType:
+ properties:
+ EmployeeName:
+ displayName: EmployeeName
+ description: Employee name whose company code needs to be fetched
+ type: String
+
+outputType:
+ properties:
+ workdayResponseTable:
+ displayName: workdayResponseTable
+ type:
+ kind: Table
+ properties:
+ CompanyCode: String
+ CompanyName: String
+ FirstName: String
+ LastName: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-CostCenter/msdyn_HRWorkdayHCMManagerDirectCostCenter.xml b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-CostCenter/msdyn_HRWorkdayHCMManagerDirectCostCenter.xml
new file mode 100644
index 00000000..291556b8
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-CostCenter/msdyn_HRWorkdayHCMManagerDirectCostCenter.xml
@@ -0,0 +1,77 @@
+
+
+
+
+ User
+
+ Tempalte_ManagerDirectCostCenter_GetWorkersManagerRequest
+ Human_Resources
+ v42.0
+
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Worker_ID']/text()
+ WorkerID
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']
+ LegalNameData
+
+
+ //*[local-name()='Worker_Organization_Data'][*[local-name()='Organization_Data']/*[local-name()='Organization_Type_Reference']/*[local-name()='ID'][@*[local-name()='type']='Organization_Type_ID' and text()='Cost_Center']]/*[local-name()='Organization_Data']/*[local-name()='Organization_Code']/text()
+ CostCenterCode
+
+
+ //*[local-name()='Worker_Organization_Data'][*[local-name()='Organization_Data']/*[local-name()='Organization_Type_Reference']/*[local-name()='ID'][@*[local-name()='type']='Organization_Type_ID' and text()='Cost_Center']]/*[local-name()='Organization_Data']/*[local-name()='Organization_Name']/text()
+ CostCenterName
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Employment_Data']/*[local-name()='Worker_Job_Data']/*[local-name()='Position_Data']/*[local-name()='Position_ID']/text()
+ PositionID
+
+
+
+
+
+
+
+
+
+
+ {ManagerOrgId}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+ false
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-CostCenter/topic.yaml b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-CostCenter/topic.yaml
new file mode 100644
index 00000000..25e1324f
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-CostCenter/topic.yaml
@@ -0,0 +1,173 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ Use this topic when a MANAGER asks about the COST CENTER / cost center code / cost center name / cost center number of THEIR DIRECT REPORTS in Workday. ONLY for direct reports (people who report to the user) — NOT for self, manager, peers, spouse, sibling, or any non-direct-report.
+
+ Triggers:
+ - "What is the cost center of my direct reports?"
+ - "Cost center of my reports / team"
+ - "Show cost centers for my direct reports"
+ - "Which cost center is each of my reports under?"
+ - "Cost center and company for my team"
+ - "Direct reports' cost center"
+
+ Result data contains a list of direct reports with their company name and cost center.
+
+ Invalid (do NOT trigger): questions about another person's cost center who isn't a direct report — manager, sister, peer. Self-cost-center questions belong to the WorkdayCostCenter topic.
+
+ Output rules:
+ - Output MUST be a nested list in markdown
+ - Use ONLY data from {Topic.workdayResponseTable}
+ - Do NOT return data if you don't have enough info
+
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: EmployeeName
+ description: Employee name whose cost center needs to be retrieved
+ entity: PersonNamePrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - Show me my team's cost center data?
+ - What cost centers are assigned to my reports?
+ - What are my team's cost centers?
+
+ actions:
+ - kind: BeginDialog
+ id: 3VGa9O
+ displayName: Check manager status
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdayManagerCheck
+
+ - kind: BeginDialog
+ id: jJpz3n
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{ManagerOrgId}"",""value"":""" & Global.ESS_UserContext_ManagerOrganizationId & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: ="msdyn_HRWorkdayHCMManagerDirectCostCenter"
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: aXVFNu
+ displayName: Parse value to a record
+ variable: Topic.workdayResponseRecord
+ valueType:
+ kind: Record
+ properties:
+ CostCenterCode:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ CostCenterName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ LegalNameData:
+ type:
+ kind: Table
+ properties:
+ Name_Detail_Data:
+ type:
+ kind: Record
+ properties:
+ "@Formatted_Name": String
+ First_Name: String
+ Last_Name: String
+
+ WorkerID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: SetVariable
+ id: setVariable_ztXmui
+ displayName: Extract data to table
+ variable: Topic.workdayResponseTable
+ value: |-
+ =ForAll(
+ Sequence(CountRows(Topic.workdayResponseRecord.WorkerID)),
+ {
+ FirstName: Last(FirstN(Topic.workdayResponseRecord.LegalNameData, Value)).Name_Detail_Data.First_Name,
+ LastName: Last(FirstN(Topic.workdayResponseRecord.LegalNameData, Value)).Name_Detail_Data.Last_Name,
+ CostCenterCode: Last(FirstN(Topic.workdayResponseRecord.CostCenterCode, Value)).Value,
+ CostCenterName: Last(FirstN(Topic.workdayResponseRecord.CostCenterName, Value)).Value
+ }
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_eEjEWp
+ conditions:
+ - id: conditionItem_yv6Ggl
+ condition: =!IsBlank(Topic.EmployeeName)
+ displayName: Check if EmployeeName provided
+ actions:
+ - kind: SetVariable
+ id: setVariable_njPUra
+ displayName: Filter for the given EmployeeName
+ variable: Topic.workdayResponseTable
+ value: =Filter(Topic.workdayResponseTable,'FirstName' = Topic.EmployeeName Or 'LastName' = Topic.EmployeeName Or Concatenate('FirstName', " ", 'LastName') = Topic.EmployeeName Or Concatenate('LastName', " ", 'FirstName') = Topic.EmployeeName)
+
+ - kind: ConditionGroup
+ id: conditionGroup_tAWzhS
+ conditions:
+ - id: conditionItem_sV8WXF
+ condition: =IsEmpty(Topic.workdayResponseTable)
+ displayName: Check is filtered data empty
+ actions:
+ - kind: SendActivity
+ id: sendActivity_3owxH6
+ displayName: Respond with no access message
+ activity: It looks like you don't have access to this information. Try making a new request.
+
+ - kind: SetVariable
+ id: setVariable_wd0UBE
+ displayName: Clear workday response
+ variable: Topic.workdayResponse
+ value: =""
+
+ - kind: EndDialog
+ id: HsUkV7
+
+ - kind: SendActivity
+ id: sendActivity_SBD1Zi
+ activity: |-
+ Here is your team's cost center information:
+ {Topic.workdayResponseTable}
+
+inputType:
+ properties:
+ EmployeeName:
+ displayName: EmployeeName
+ description: Employee name whose cost center needs to be retrieved
+ type: String
+
+outputType:
+ properties:
+ workdayResponseTable:
+ displayName: workdayResponseTable
+ type:
+ kind: Table
+ properties:
+ CostCenterCode: String
+ CostCenterName: String
+ FirstName: String
+ LastName: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-Jobtaxanomy/msdyn_HRWorkdayHCMManagerJobTaxonomy.xml b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-Jobtaxanomy/msdyn_HRWorkdayHCMManagerJobTaxonomy.xml
new file mode 100644
index 00000000..a447ffeb
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-Jobtaxanomy/msdyn_HRWorkdayHCMManagerJobTaxonomy.xml
@@ -0,0 +1,77 @@
+
+
+
+
+ User
+
+ msdyn_HRWorkdayHCMManagerJobTaxonomy_GetWorkersManagerRequest
+ Human_Resources
+ v41.0
+
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Worker_ID']/text()
+ WorkerID
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/*[local-name()='First_Name']/text()
+
+ FirstName
+
+
+ //*[local-name()='Worker_Data']/*[local-name()='Personal_Data']/*[local-name()='Name_Data']/*[local-name()='Legal_Name_Data']/*[local-name()='Name_Detail_Data']/*[local-name()='Last_Name']/text()
+ LastName
+
+
+ //*[local-name()="Position_Title"]/text()
+ JobTitle
+
+
+ //*[local-name()="Business_Title"]/text()
+ BusinessTitle
+
+
+ //*[local-name()="Job_Profile_Name"]/text()
+ JobProfile
+
+
+ //*[local-name()='Job_Profile_Summary_Data']/*[local-name()='Job_Family_Reference']/*[local-name()='ID' and @*[local-name()='type']='Job_Family_ID']/text()
+ JobFamilyId
+
+
+
+
+
+
+
+
+
+
+ {ManagerOrgId}
+
+
+
+ {As_Of_Effective_Date}
+
+
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-Jobtaxanomy/topic.yaml b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-Jobtaxanomy/topic.yaml
new file mode 100644
index 00000000..f9473ba8
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/ManagerScenarios/WorkdayManagersdirect-Jobtaxanomy/topic.yaml
@@ -0,0 +1,216 @@
+kind: AdaptiveDialog
+modelDescription: |-
+ Use this topic when a MANAGER asks about the JOB FUNCTION / job family / job profile / job classification / job taxonomy / job code / external title / internal title / job category of THEIR DIRECT REPORTS in Workday. ONLY for direct reports — NOT for self, manager, peers, spouse, sibling, or any non-direct-report.
+
+ Triggers:
+ - "What is the external title of my direct reports?"
+ - "What is [EmployeeName]'s job title?"
+ - "Job function / job family for my reports"
+ - "Show job classifications for my team"
+ - "Direct reports' job taxonomy"
+
+ Result data is a list of direct reports with their job function fields.
+
+ do NOT trigger for self-job-taxonomy questions belong to the ViewJobTaxonomy topic.
+
+ Output rules:
+ - Output MUST be a nested list in markdown
+ - Use ONLY data from {Topic.workdayResponseTable}
+ - Do NOT return data if you don't have enough info
+
+inputs:
+ - kind: AutomaticTaskInput
+ propertyName: EmployeeName
+ description: Employee name whose job data needs to be retrieved
+ entity: PersonNamePrebuiltEntity
+ shouldPromptUser: false
+ inputSettings:
+ repeatCount: 0
+ defaultValue: =Blank()
+
+beginDialog:
+ kind: OnRecognizedIntent
+ id: main
+ intent:
+ triggerQueries:
+ - Show me my team's job title?
+ - What is the internal title of my direct report X?
+ - What is the job function of my team?
+ - What job profile is mapped to my team members?
+ - What are the external titles of my team members?
+ - What is the internal title of my direct report [EmployeeName]?
+ - Show me my team's job taxonomy?
+ - What is the job function of my team?
+ - What job profile is mapped to my team members?
+ - What are the external titles of my team members?
+ - Get job data for [EmployeeName].
+ - What is the job title of [EmployeeName]?
+
+ actions:
+ - kind: BeginDialog
+ id: Y8gqWh
+ displayName: Check manager status
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdayManagerCheck
+
+ - kind: BeginDialog
+ id: 7Va4UU
+ displayName: Redirect to Workday Get Common Execution
+ input:
+ binding:
+ parameters: ="{""params"":[{""key"":""{ManagerOrgId}"",""value"":""" & Global.ESS_UserContext_ManagerOrganizationId & """},{""key"":""{As_Of_Effective_Date}"",""value"":"""& Text(Today(), "yyyy-MM-dd") &"""}]}"
+ scenarioName: msdyn_HRWorkdayHCMManagerJobTaxonomy
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemGetCommonExecution
+ output:
+ binding:
+ errorResponse: Topic.errorResponse
+ isSuccess: Topic.isSuccess
+ workdayResponse: Topic.workdayResponse
+
+ - kind: ParseValue
+ id: mq6jr0
+ displayName: Parse workdayResponse into table
+ variable: Topic.WorkdayResponseRecord
+ valueType:
+ kind: Record
+ properties:
+ BusinessTitle:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ FirstName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ JobFamilyId:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ JobProfile:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ JobTitle:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ LastName:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ WorkerID:
+ type:
+ kind: Table
+ properties:
+ Value: String
+
+ value: =Topic.workdayResponse
+
+ - kind: BeginDialog
+ id: QdE53O
+ displayName: Refresh JobFamily reference data
+ input:
+ binding:
+ IsTableEmpty: =IsBlank(Global.JobFamilyLookupTable)
+ ReferenceDataKey: Job_Family_ID
+
+ dialog: msdyn_copilotforemployeeselfservicedahr.topic.WorkdaySystemRefreshReferenceData
+
+ - kind: SetVariable
+ id: setVariable_EFFdlM
+ displayName: Merge all records in table
+ variable: Topic.workdayResponseTable
+ value: |-
+ =ForAll(
+ Sequence(CountRows(Topic.WorkdayResponseRecord.WorkerID)),
+ {
+ FirstName: Last(FirstN(Topic.WorkdayResponseRecord.FirstName, Value)).Value,
+ LastName: Last(FirstN(Topic.WorkdayResponseRecord.LastName, Value)).Value,
+ JobTitle: Last(FirstN(Topic.WorkdayResponseRecord.JobTitle, Value)).Value,
+ BusinessTitle: Last(FirstN(Topic.WorkdayResponseRecord.BusinessTitle, Value)).Value,
+ JobProfile: Last(FirstN(Topic.WorkdayResponseRecord.JobProfile, Value)).Value,
+ JobFamily: LookUp(Global.JobFamilyLookupTable,
+ ID = Last(FirstN(Topic.WorkdayResponseRecord.JobFamilyId, Value)).Value
+ ).Referenced_Object_Descriptor
+ }
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_yepfHF
+ conditions:
+ - id: conditionItem_86c1ij
+ condition: =!IsBlank(Topic.EmployeeName)
+ displayName: Check if EmployeeName provided
+ actions:
+ - kind: SetVariable
+ id: setVariable_z1fKB1
+ displayName: Filter for the given EmployeeName
+ variable: Topic.workdayResponseTable
+ value: |
+ =Filter(
+ Topic.workdayResponseTable,
+ FirstName = Topic.EmployeeName Or
+ LastName = Topic.EmployeeName Or
+ Concatenate(FirstName, " ", LastName) = Topic.EmployeeName Or
+ Concatenate(LastName, " ", FirstName) = Topic.EmployeeName
+ )
+
+ - kind: ConditionGroup
+ id: conditionGroup_BVYTlV
+ conditions:
+ - id: conditionItem_QmVzq2
+ condition: =IsEmpty(Topic.workdayResponseTable)
+ displayName: Check is filtered data empty
+ actions:
+ - kind: SendActivity
+ id: sendActivity_m8xRtz
+ displayName: Respond with no access message
+ activity: It looks like you don't have access to this information. Try making a new request.
+
+ - kind: SetVariable
+ id: setVariable_wd0UBE
+ displayName: Clear workday response
+ variable: Topic.workdayResponse
+ value: =""
+
+ - kind: EndDialog
+ id: xa93ze
+
+ - kind: SendActivity
+ id: sendActivity_DHNIyX
+ activity: |-
+ Here is your team's job information:
+ {Topic.workdayResponseTable}
+
+inputType:
+ properties:
+ EmployeeName:
+ displayName: EmployeeName
+ description: Employee name whose job data needs to be retrieved
+ type: String
+
+outputType:
+ properties:
+ workdayResponseTable:
+ displayName: workdayResponseTable
+ type:
+ kind: Table
+ properties:
+ BusinessTitle: String
+ FirstName: String
+ JobFamily: String
+ JobProfile: String
+ JobTitle: String
+ LastName: String
\ No newline at end of file
diff --git a/EmployeeSelfServiceAgent/WorkdayDA/README.md b/EmployeeSelfServiceAgent/WorkdayDA/README.md
new file mode 100644
index 00000000..141c9e0a
--- /dev/null
+++ b/EmployeeSelfServiceAgent/WorkdayDA/README.md
@@ -0,0 +1,29 @@
+---
+title: Workday
+parent: Employee Self-Service
+nav_order: 1
+---
+# ESS Workday Scenarios
+
+This folder contains sample topic definitions and ESS Template configurations XML that customers can use to extend the functionality of their ESS Agent setup. Use the topic definitions (`topic.yaml`) and the accompanying Template configuration XML file to create new topics in your environment or to customize the behavior of existing topics for the scenarios listed below.
+
+Usage notes:
+- Each scenario folder contains a `topic.yaml` (the Copilot/ESS topic) and a template configuration used by the topic.
+- Copy `topic.yaml` into your Copilot topic catalog and ensure the template configuration is added to the Employee Self Service Template Configuration.
+- Update parameter bindings (for example employee id, manager org id, effective date) to match your runtime context.
+- The topic `.yaml` files include trigger queries (sample prompts). Use those as seeds for testing.
+
+Below is a consolidated table that lists each scenario, a short description, and sample prompt(s) you can use to test the topic.
+
+| Scenario | Description | Sample prompt(s) |
+|---|---|---|
+| `EmployeeGetVacationBalance` | Returns the requesting user's vacation balance information from Workday. Displays available time off that can be taken. | "What is my vacation balance?"
"How much time off can I take?"
"What is my workday vacation balance?" |
+| `WorkdayEmployeeRequestTimeOff` | Allows employees to submit time off requests for themselves through Workday. Prompts for necessary details like dates and hours. | "Request 8 hours vacation on 2025-09-15"
"I want to request time off"
"Submit vacation request" |
+| `WorkdayEmployeesviewtheirjobtaxonomy` | Responds to requests about the requesting user's job taxonomy (job title, job function, job profile). | "What is my job title?"
"What is my external title?" |
+| `WorkdayGetContactInformation` | Returns the requesting user's contact information (work/home phones, emails, addresses). | "What is my Work Phone?"
"Show my Home Email" |
+| `WorkdayGetEducation` | Returns the requesting user's education history (school, degree, field of study, years attended). | "Show my Education Details"
"What was my field of study?" |
+| `WorkdayGetGovernmentIDs` | Returns government ID information associated with the requesting user's profile (ID types, issued/expiration dates, country). | "What are my Government Ids?" |
+| `WorkdayManagersdirect-CompanyCode` | Returns company code and company name for employees who directly report to the requesting user (manager view). Output is produced as a nested markdown list. | "What are the company codes for my reports?" |
+| `WorkdayManagersdirect-CostCenter` | Returns cost center details for direct reports of the requesting user. Output is produced as a nested markdown list. | "What is the cost center of my direct reports?" |
+| `WorkdayManagersdirect-Jobtaxanomy` | Returns job taxonomy (job title, business title, job profile, job family) for the manager's direct reports. Output is produced as a nested markdown list. | "Show me my team's job title"
"What is the job title of [EmployeeName]?" |
+| `WorkdayManagerServiceAnniversary` | Returns upcoming service anniversaries for a manager's direct reports. The topic returns a markdown table with Employee Name, Hire Date, Upcoming Service Anniversary Date, Upcoming Milestone. | "When are the service anniversaries of all my directs?"
"What is [EmployeeName]'s next service anniversary?" |
\ No newline at end of file
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 00000000..9ba155b1
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,6 @@
+source 'https://rubygems.org'
+gem "jekyll", "~> 4.4"
+gem "just-the-docs", "~> 0.12"
+gem "jekyll-readme-index", "~> 0.3"
+gem "jekyll-seo-tag"
+gem "jekyll-include-cache"
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 00000000..b0aba201
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,178 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ addressable (2.8.9)
+ public_suffix (>= 2.0.2, < 8.0)
+ base64 (0.3.0)
+ bigdecimal (4.0.1)
+ colorator (1.1.0)
+ concurrent-ruby (1.3.6)
+ csv (3.3.5)
+ em-websocket (0.5.3)
+ eventmachine (>= 0.12.9)
+ http_parser.rb (~> 0)
+ eventmachine (1.2.7)
+ ffi (1.17.3)
+ ffi (1.17.3-aarch64-linux-gnu)
+ ffi (1.17.3-aarch64-linux-musl)
+ ffi (1.17.3-arm-linux-gnu)
+ ffi (1.17.3-arm-linux-musl)
+ ffi (1.17.3-arm64-darwin)
+ ffi (1.17.3-x86-linux-gnu)
+ ffi (1.17.3-x86-linux-musl)
+ ffi (1.17.3-x86_64-darwin)
+ ffi (1.17.3-x86_64-linux-gnu)
+ ffi (1.17.3-x86_64-linux-musl)
+ forwardable-extended (2.6.0)
+ google-protobuf (4.34.0)
+ bigdecimal
+ rake (~> 13.3)
+ google-protobuf (4.34.0-aarch64-linux-gnu)
+ bigdecimal
+ rake (~> 13.3)
+ google-protobuf (4.34.0-aarch64-linux-musl)
+ bigdecimal
+ rake (~> 13.3)
+ google-protobuf (4.34.0-arm64-darwin)
+ bigdecimal
+ rake (~> 13.3)
+ google-protobuf (4.34.0-x86-linux-gnu)
+ bigdecimal
+ rake (~> 13.3)
+ google-protobuf (4.34.0-x86-linux-musl)
+ bigdecimal
+ rake (~> 13.3)
+ google-protobuf (4.34.0-x86_64-darwin)
+ bigdecimal
+ rake (~> 13.3)
+ google-protobuf (4.34.0-x86_64-linux-gnu)
+ bigdecimal
+ rake (~> 13.3)
+ google-protobuf (4.34.0-x86_64-linux-musl)
+ bigdecimal
+ rake (~> 13.3)
+ http_parser.rb (0.8.1)
+ i18n (1.14.8)
+ concurrent-ruby (~> 1.0)
+ jekyll (4.4.1)
+ addressable (~> 2.4)
+ base64 (~> 0.2)
+ colorator (~> 1.0)
+ csv (~> 3.0)
+ em-websocket (~> 0.5)
+ i18n (~> 1.0)
+ jekyll-sass-converter (>= 2.0, < 4.0)
+ jekyll-watch (~> 2.0)
+ json (~> 2.6)
+ kramdown (~> 2.3, >= 2.3.1)
+ kramdown-parser-gfm (~> 1.0)
+ liquid (~> 4.0)
+ mercenary (~> 0.3, >= 0.3.6)
+ pathutil (~> 0.9)
+ rouge (>= 3.0, < 5.0)
+ safe_yaml (~> 1.0)
+ terminal-table (>= 1.8, < 4.0)
+ webrick (~> 1.7)
+ jekyll-include-cache (0.2.1)
+ jekyll (>= 3.7, < 5.0)
+ jekyll-readme-index (0.3.0)
+ jekyll (>= 3.0, < 5.0)
+ jekyll-sass-converter (3.1.0)
+ sass-embedded (~> 1.75)
+ jekyll-seo-tag (2.8.0)
+ jekyll (>= 3.8, < 5.0)
+ jekyll-watch (2.2.1)
+ listen (~> 3.0)
+ json (2.19.1)
+ just-the-docs (0.12.0)
+ jekyll (>= 3.8.5)
+ jekyll-include-cache
+ jekyll-seo-tag (>= 2.0)
+ rake (>= 12.3.1)
+ kramdown (2.5.2)
+ rexml (>= 3.4.4)
+ kramdown-parser-gfm (1.1.0)
+ kramdown (~> 2.0)
+ liquid (4.0.4)
+ listen (3.10.0)
+ logger
+ rb-fsevent (~> 0.10, >= 0.10.3)
+ rb-inotify (~> 0.9, >= 0.9.10)
+ logger (1.7.0)
+ mercenary (0.4.0)
+ pathutil (0.16.2)
+ forwardable-extended (~> 2.6)
+ public_suffix (7.0.5)
+ rake (13.3.1)
+ rb-fsevent (0.11.2)
+ rb-inotify (0.11.1)
+ ffi (~> 1.0)
+ rexml (3.4.4)
+ rouge (4.7.0)
+ safe_yaml (1.0.5)
+ sass-embedded (1.98.0)
+ google-protobuf (~> 4.31)
+ rake (>= 13)
+ sass-embedded (1.98.0-aarch64-linux-android)
+ google-protobuf (~> 4.31)
+ sass-embedded (1.98.0-aarch64-linux-gnu)
+ google-protobuf (~> 4.31)
+ sass-embedded (1.98.0-aarch64-linux-musl)
+ google-protobuf (~> 4.31)
+ sass-embedded (1.98.0-arm-linux-androideabi)
+ google-protobuf (~> 4.31)
+ sass-embedded (1.98.0-arm-linux-gnueabihf)
+ google-protobuf (~> 4.31)
+ sass-embedded (1.98.0-arm-linux-musleabihf)
+ google-protobuf (~> 4.31)
+ sass-embedded (1.98.0-arm64-darwin)
+ google-protobuf (~> 4.31)
+ sass-embedded (1.98.0-riscv64-linux-android)
+ google-protobuf (~> 4.31)
+ sass-embedded (1.98.0-riscv64-linux-gnu)
+ google-protobuf (~> 4.31)
+ sass-embedded (1.98.0-riscv64-linux-musl)
+ google-protobuf (~> 4.31)
+ sass-embedded (1.98.0-x86_64-darwin)
+ google-protobuf (~> 4.31)
+ sass-embedded (1.98.0-x86_64-linux-android)
+ google-protobuf (~> 4.31)
+ sass-embedded (1.98.0-x86_64-linux-gnu)
+ google-protobuf (~> 4.31)
+ sass-embedded (1.98.0-x86_64-linux-musl)
+ google-protobuf (~> 4.31)
+ terminal-table (3.0.2)
+ unicode-display_width (>= 1.1.1, < 3)
+ unicode-display_width (2.6.0)
+ webrick (1.9.2)
+
+PLATFORMS
+ aarch64-linux-android
+ aarch64-linux-gnu
+ aarch64-linux-musl
+ arm-linux-androideabi
+ arm-linux-gnu
+ arm-linux-gnueabihf
+ arm-linux-musl
+ arm-linux-musleabihf
+ arm64-darwin
+ riscv64-linux-android
+ riscv64-linux-gnu
+ riscv64-linux-musl
+ ruby
+ x86-linux-gnu
+ x86-linux-musl
+ x86_64-darwin
+ x86_64-linux-android
+ x86_64-linux-gnu
+ x86_64-linux-musl
+
+DEPENDENCIES
+ jekyll (~> 4.4)
+ jekyll-include-cache
+ jekyll-readme-index (~> 0.3)
+ jekyll-seo-tag
+ just-the-docs (~> 0.12)
+
+BUNDLED WITH
+ 2.7.2
diff --git a/HumanVerificationSample/Images/Screenshot_1.jpg b/HumanVerificationSample/Images/Screenshot_1.jpg
deleted file mode 100644
index f9a1f734..00000000
Binary files a/HumanVerificationSample/Images/Screenshot_1.jpg and /dev/null differ
diff --git a/HumanVerificationSample/Images/Screenshot_10.jpg b/HumanVerificationSample/Images/Screenshot_10.jpg
deleted file mode 100644
index 30667e7b..00000000
Binary files a/HumanVerificationSample/Images/Screenshot_10.jpg and /dev/null differ
diff --git a/HumanVerificationSample/Images/Screenshot_2.jpg b/HumanVerificationSample/Images/Screenshot_2.jpg
deleted file mode 100644
index e2cfb6d0..00000000
Binary files a/HumanVerificationSample/Images/Screenshot_2.jpg and /dev/null differ
diff --git a/HumanVerificationSample/Images/Screenshot_3.jpg b/HumanVerificationSample/Images/Screenshot_3.jpg
deleted file mode 100644
index 94f310d7..00000000
Binary files a/HumanVerificationSample/Images/Screenshot_3.jpg and /dev/null differ
diff --git a/HumanVerificationSample/Images/Screenshot_4.jpg b/HumanVerificationSample/Images/Screenshot_4.jpg
deleted file mode 100644
index 1c37b044..00000000
Binary files a/HumanVerificationSample/Images/Screenshot_4.jpg and /dev/null differ
diff --git a/HumanVerificationSample/Images/Screenshot_5.jpg b/HumanVerificationSample/Images/Screenshot_5.jpg
deleted file mode 100644
index 82d9261a..00000000
Binary files a/HumanVerificationSample/Images/Screenshot_5.jpg and /dev/null differ
diff --git a/HumanVerificationSample/Images/Screenshot_6.jpg b/HumanVerificationSample/Images/Screenshot_6.jpg
deleted file mode 100644
index f99832bf..00000000
Binary files a/HumanVerificationSample/Images/Screenshot_6.jpg and /dev/null differ
diff --git a/HumanVerificationSample/Images/Screenshot_7.jpg b/HumanVerificationSample/Images/Screenshot_7.jpg
deleted file mode 100644
index 2c5b429d..00000000
Binary files a/HumanVerificationSample/Images/Screenshot_7.jpg and /dev/null differ
diff --git a/HumanVerificationSample/Images/Screenshot_8.jpg b/HumanVerificationSample/Images/Screenshot_8.jpg
deleted file mode 100644
index a29e93bd..00000000
Binary files a/HumanVerificationSample/Images/Screenshot_8.jpg and /dev/null differ
diff --git a/HumanVerificationSample/Images/Screenshot_9.jpg b/HumanVerificationSample/Images/Screenshot_9.jpg
deleted file mode 100644
index bb18e979..00000000
Binary files a/HumanVerificationSample/Images/Screenshot_9.jpg and /dev/null differ
diff --git a/HumanVerificationSample/Images/code-length.png b/HumanVerificationSample/Images/code-length.png
deleted file mode 100644
index fd455cff..00000000
Binary files a/HumanVerificationSample/Images/code-length.png and /dev/null differ
diff --git a/HumanVerificationSample/Images/email-verification-bot.png b/HumanVerificationSample/Images/email-verification-bot.png
deleted file mode 100644
index ddaf8bc0..00000000
Binary files a/HumanVerificationSample/Images/email-verification-bot.png and /dev/null differ
diff --git a/HumanVerificationSample/Images/howto1.png b/HumanVerificationSample/Images/howto1.png
deleted file mode 100644
index bdd452f6..00000000
Binary files a/HumanVerificationSample/Images/howto1.png and /dev/null differ
diff --git a/HumanVerificationSample/Images/howto10.png b/HumanVerificationSample/Images/howto10.png
deleted file mode 100644
index 54432025..00000000
Binary files a/HumanVerificationSample/Images/howto10.png and /dev/null differ
diff --git a/HumanVerificationSample/Images/howto11.png b/HumanVerificationSample/Images/howto11.png
deleted file mode 100644
index eab46ac1..00000000
Binary files a/HumanVerificationSample/Images/howto11.png and /dev/null differ
diff --git a/HumanVerificationSample/Images/howto2.png b/HumanVerificationSample/Images/howto2.png
deleted file mode 100644
index b9a616ac..00000000
Binary files a/HumanVerificationSample/Images/howto2.png and /dev/null differ
diff --git a/HumanVerificationSample/Images/howto3.png b/HumanVerificationSample/Images/howto3.png
deleted file mode 100644
index 09a61365..00000000
Binary files a/HumanVerificationSample/Images/howto3.png and /dev/null differ
diff --git a/HumanVerificationSample/Images/howto4.png b/HumanVerificationSample/Images/howto4.png
deleted file mode 100644
index ff07658c..00000000
Binary files a/HumanVerificationSample/Images/howto4.png and /dev/null differ
diff --git a/HumanVerificationSample/Images/howto5.png b/HumanVerificationSample/Images/howto5.png
deleted file mode 100644
index fd16ab94..00000000
Binary files a/HumanVerificationSample/Images/howto5.png and /dev/null differ
diff --git a/HumanVerificationSample/Images/howto6.png b/HumanVerificationSample/Images/howto6.png
deleted file mode 100644
index 567458c0..00000000
Binary files a/HumanVerificationSample/Images/howto6.png and /dev/null differ
diff --git a/HumanVerificationSample/Images/howto7.png b/HumanVerificationSample/Images/howto7.png
deleted file mode 100644
index e23de829..00000000
Binary files a/HumanVerificationSample/Images/howto7.png and /dev/null differ
diff --git a/HumanVerificationSample/Images/howto8.png b/HumanVerificationSample/Images/howto8.png
deleted file mode 100644
index 454a8294..00000000
Binary files a/HumanVerificationSample/Images/howto8.png and /dev/null differ
diff --git a/HumanVerificationSample/Images/howto9.png b/HumanVerificationSample/Images/howto9.png
deleted file mode 100644
index afda9e91..00000000
Binary files a/HumanVerificationSample/Images/howto9.png and /dev/null differ
diff --git a/HumanVerificationSample/Images/import-solution.png b/HumanVerificationSample/Images/import-solution.png
deleted file mode 100644
index e955febc..00000000
Binary files a/HumanVerificationSample/Images/import-solution.png and /dev/null differ
diff --git a/HumanVerificationSample/README.md b/HumanVerificationSample/README.md
deleted file mode 100644
index a18b7e76..00000000
--- a/HumanVerificationSample/README.md
+++ /dev/null
@@ -1,116 +0,0 @@
-# Human Verification Sample
-
-## Overview
-
-In this sample, we will show how a Bot by using Power Virtual Agents can identify if the user on the other side is a Human or a Bot.
-This can be achieved by sending an email to the user with a verification code that has to be used on the Bot in order to proceed with the dialog flow.
-
-## Prerequisites
-
-* Email account to send emails from
-
-## Importing the solution
-
-To import the sample bot follow the steps below:
-
-1. Log in to the [Power Apps](make.powerapps.com) site.
-
-2. Select the environment where the PVA sample bot will be imported.
-
-
-
-3. Go to the Solutions tab, and on the command bar, select the Import option.
-
-
-
-4. Click on the Browse button and locate the HumanVerificationSample_1_0_0_0.zip file included in this repository.
-
-
-
-5. Information about the solution will be displayed, click Next
-
-
-
-6. If you have an Office connection, select it. Otherwise, create a new connection. After selecting the connection, the Import button will be available.
-
-
-
-7. After clicking the import button, the solution will start to import into the selected environment.
-
-
-
-8. When the import process has finished, the following message will appear.
-
-
-
-## How to setup code verification
-
-To set up the sample bot, some configurations need to be done to start using it.
-
-1. Go to [Power Virtual Agents](https://web.powerva.microsoft.com/), select the environment and click on the EmailVerificationBot.
-
-
-
-2. Go to the "Human Verification" topic.
-
-3. Go to the "Generate the verification code" flow and click on "View flow details".
-
-
-
-4. After being redirected to power automate, click the "Edit" button.
-
-
-
-5. On the "Initialize CodeLength variable" step, you can change the value to set the desired length of the verification code that will be sent through email.
-
-
-
-6. On the "Send an email to the account sent as input" step, an email account from which the verification code will be sent, has to be set up. The placeholder "youremail@here" should be replaced.
-
-
-
- Note: Make sure to use the same email account used by the connection reference when importing the solution.
-
-## How to use the sample
-
-1. One of these trigger phrases has to be used to start the flow:
- - human verification
- - email verification
- - verification
- - verification code
-
-2. The bot will ask for the user's email to validate that there is a person on the other side
-
-
-
-3. The bot will send a verification code through email and the user needs to copy the code and paste it into the chat window
-
-
-
-4. Once the email is entered, there can be two different scenarios:
-
- a) The verification code is correct and the chat flow finishes.
-
- 
-
- b) The entered code is not valid.
-
- 
-
-## How does the verification code flow work
-
-1. First, the Mail variable gets filled with the email account provided by the user during the conversation flow.
-
-
-
-2. Then, the CodeLength and VerificationCode variables are initialized.
-
-
-
-3. After that, the email is composed to be sent to the email account informed by the user, the subject is set as "PVA verification code" and finally the body is formed by the text "Verification code:" and followed by the value of the VerificationCode variable.
-
-
-
-4. Finally, the value of the VerificationCode variable is returned to the bot to be used in a prior step.
-
-
diff --git a/HumanVerificationSample/Solution/HumanVerificationSample_1_0_0_0.zip b/HumanVerificationSample/Solution/HumanVerificationSample_1_0_0_0.zip
deleted file mode 100644
index d96487d7..00000000
Binary files a/HumanVerificationSample/Solution/HumanVerificationSample_1_0_0_0.zip and /dev/null differ
diff --git a/ImplementationGuide/Microsoft Copilot Studio - Implementation Guide (1.4).pptx b/ImplementationGuide/Microsoft Copilot Studio - Implementation Guide (1.4).pptx
deleted file mode 100644
index 023e22ee..00000000
Binary files a/ImplementationGuide/Microsoft Copilot Studio - Implementation Guide (1.4).pptx and /dev/null differ
diff --git a/ImplementationGuide/README.md b/ImplementationGuide/README.md
deleted file mode 100644
index 131e0f01..00000000
--- a/ImplementationGuide/README.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# What is the implementation guide?
-
-The Copilot Studio implementation guide offers in-depth guidance, best practices, and reference architectures and patterns. It is designed to help customers, partners, and Microsoft teams plan, design, and review copilot projects and architectures for a successful implementation journey.
-
-The implementation guide also provides a framework to do a 360-degree review of a project. Through probing questions, it highlights potential risks and gaps, aims at aligning the project with the product roadmap, and shares guidance, best practices and reference architecture examples.
-Users of the implementation review are guided at each step of the journey with the side pane that provides more context on the questions or features, shares best practices, and includes links to additional resources.
-
-Inspired by the [Success by Design](https://learn.microsoft.com/dynamics365/guidance/implementation-guide/success-by-design) framework – a tried and tested Microsoft methodology used for Business Applications implementations – this format aims to detect bad patterns, identify risks, share best practices, and showcase example implementations.
-
-# Where can I get the implementation guide?
-
-Use this URL to download the latest version of the guide: [aka.ms/CopilotStudioImplementationGuide](https://aka.ms/CopilotStudioImplementationGuide)
-
-# What are the areas covered by the guide?
-
-The Copilot Studio implementation guide covers these chapters:
-- Overview of the project
-- Architecture overview
-- Language
-- AI functionalities
-- Integrations
-- Channels and hand off
-- Security, monitoring & governance
-- Application lifecycle management
-- Analytics & KPIs
-- Gaps & top requests
-- Dynamics 365 Omnichannel (optional)
-
-# Version history
-
-| Version | Date | Updates |
-| --- | --- | --- |
-| 1.4 | March 5, 2024 | - Divided the **Integrations and channels** chapter into 2 chapters.
- Reworked the **Generative answers processes and considerations** slide to surface the Query rewriting step and also highlight the validation that occurs at each steps. This slide also contains footnotes detailing the process.
- Added a new **Generative AI security and compliance considerations** slide.
Added an **Integration patterns and considerations** slide.
- Added a **Security, copilot, & user management** slide, detailing best practices to secure a Copilot Studio project.
- Added a **Copilot Studio security roles** slide. |
-| 1.3 | January 9, 2024 | - Added 2 patterns for live agent hand off.
- Added the **Security and administration controls** slide. |
-| 1.2 | December 6, 2023 | - Minor edits. |
-| 1.1 | December 5, 2023 | - Initial version. |
-| 1.0 | 2023 | - Unpublished version. |
diff --git a/MIGRATION.md b/MIGRATION.md
new file mode 100644
index 00000000..288d41a6
--- /dev/null
+++ b/MIGRATION.md
@@ -0,0 +1,88 @@
+# Sample Migration Guide
+
+This table maps old folder locations (on `main`) to new locations (on `reorg/v1`). If you've linked to these samples in blogs, videos, or docs, please update your links.
+
+**Live site**: https://microsoft.github.io/CopilotStudioSamples/
+
+## Authoring
+
+| Sample | Old Location | New Location | Owner |
+|--------|-------------|-------------|-------|
+| Adaptive Card Snippets | `AdaptiveCardSamples` | `authoring/snippets/adaptive-cards` | Henry Jammes |
+| Citation Swap | `OnGeneratedResponse` | `authoring/snippets/topics/citation-swap` | Remi Dyon |
+| Account Contact Lookup | `ExampleAgents/AccountContactLookupAgent` | `authoring/solutions/account-contact-lookup` | Dewain Robinson |
+| Auto Detect Language | `AutoDetectLanguageSample` | `authoring/solutions/auto-detect-language` | Dewain Robinson |
+| Dataverse Indexer | `DataverseIndexer` | `authoring/solutions/dataverse-indexer` | Henry Jammes |
+| Feedback Analyzer | `FeedbackAnalyzer` | `authoring/solutions/feedback-analyzer` | rafalcaraz |
+| Generative Chitchat | `GenerativeChitChat` | `authoring/solutions/generative-chitchat` | Dewain Robinson |
+| Resume Job Finder | `ResumeJobFinderAgent` | `authoring/solutions/resume-job-finder` | Dewain Robinson |
+
+## Contact Center
+
+| Sample | Old Location | New Location | Owner |
+|--------|-------------|-------------|-------|
+| Skill Handoff | `IntegrateWithEngagementHub/HandoverToLiveAgentUsingSkill` | `contact-center/servicenow/HandoverToLiveAgentUsingSkill` | adilei |
+| Salesforce | `IntegrateWithEngagementHub/Salesforce` | `contact-center/servicenow/Salesforce` | adilei |
+| ServiceNow | `IntegrateWithEngagementHub/ServiceNow` | `contact-center/servicenow/ServiceNow` | adilei |
+| Engagement Playbook | `Playbook` | *(removed)* | Matt Farmer |
+
+## Extensibility
+
+| Sample | Old Location | New Location | Owner |
+|--------|-------------|-------------|-------|
+| Simple A2A Sample | `A2ASamples/Simple-A2A-Sample` | `extensibility/a2a/Simple-A2A-Sample` | Giorgio Ughini |
+| Call Agent Connector | `CallAgentConnector` | `extensibility/agents-sdk/call-agent-connector` | adilei |
+| Multilingual Bot | `MultilingualBotSample` | `extensibility/agents-sdk/multilingual-bot` *(deprecated)* | adilei |
+| Relay Bot | `RelayBotSample` | `extensibility/agents-sdk/relay-bot` *(deprecated)* | tracyboehrer |
+| Pass Resources as Inputs | `MCPSamples/pass-resources-as-inputs` | `extensibility/mcp/pass-resources-as-inputs` | Giorgio Ughini |
+| Search Species Resources | `MCPSamples/search-species-resources-typescript` | `extensibility/mcp/search-species-resources-typescript` | adilei |
+
+## Guides
+
+| Sample | Old Location | New Location | Owner |
+|--------|-------------|-------------|-------|
+| Implementation Guide | `ImplementationGuide` | `guides/implementation-guide` | Henry Jammes |
+| Workshop | `CopilotStudioWorkshop` | `guides/workshop` | Henry Jammes |
+
+## Infrastructure
+
+| Sample | Old Location | New Location | Owner |
+|--------|-------------|-------------|-------|
+| VNet Support | `VNet support` | `infrastructure/vnet-support` | Iaan D'Souza-Wiltshire |
+
+## SSO
+
+| Sample | Old Location | New Location | Owner |
+|--------|-------------|-------------|-------|
+| Entra ID SSO | `SSOSamples/SSOwithEntraID` | `sso/entra-id` | adilei |
+| Okta SSO | `SSOSamples/3rdPartySSOWithOKTA` | `sso/okta` | adilei |
+| Chat API | `SSOSamples/CopilotStudioChatAPI` | *(removed)* | adilei |
+
+## Testing
+
+| Sample | Old Location | New Location | Owner |
+|--------|-------------|-------------|-------|
+| Pytest Agents SDK | `FunctionalTesting/PytestAgentsSDK` | `testing/functional/PytestAgentsSDK` | adilei |
+| Response Analysis | `FunctionalTesting/ResponseAnalysisAgentsSDK` | `testing/functional/ResponseAnalysisAgentsSDK` | kaul-vineet |
+| JMeter Multi Thread Group | `LoadTesting/JMeterMultiThreadGroup` | `testing/load/JMeterMultiThreadGroup` | adilei |
+
+## UI — Custom UI
+
+| Sample | Old Location | New Location | Owner |
+|--------|-------------|-------------|-------|
+| Assistant UI | `AssistantUICopilotStudioClient` | `ui/custom-ui/assistant-ui/assistant-ui-mcs` | adilei |
+| DirectLine JS | `DirectLineJSSample` | `ui/custom-ui/directline-js` | adilei |
+| Reasoning Display | `ShowReasoningSample` | `ui/custom-ui/reasoning-display` | Giorgio Ughini |
+
+## UI — Embed
+
+| Sample | Old Location | New Location | Owner |
+|--------|-------------|-------------|-------|
+| D365 CS + Okta | `SSOSamples/3rdPartySSOWithOKTA - Copilot+D365OC` | `ui/embed/d365-cs-okta` | kaul-vineet |
+| D365 CS + SharePoint | `SSOSamples/DynamicsLiveChatSSO` | `ui/embed/d365-cs-sharepoint` | Jody Boelen |
+| Minimizable Widget | `CustomExternalUI` | `ui/embed/minimizable-widget` | Robin |
+| PCF Canvas App | `PCFControls/ChatControl` | `ui/embed/pcf-canvas-app` | Dieter De Cock |
+| ServiceNow Widget | `ServiceNowWidget` | `ui/embed/servicenow-widget` | adilei |
+| SharePoint Customizer | `SSOSamples/SharePointSSOComponent` | `ui/embed/sharepoint-customizer` | Henry Jammes |
+| SharePoint SSO App Customizer | `SSOSamples/SharePointSSOAppCustomizer` | `ui/embed/sharepoint-customizer/SharePointSSOAppCustomizer` | Giorgio Ughini |
+| Typeahead Suggestions | `TypeaheadSuggestions` | `ui/embed/typeahead-suggestions` | Parag Dessai |
diff --git a/MultilingualBotSample/Bot/DeploymentTemplates/DeployUseExistResourceGroup/readme.md b/MultilingualBotSample/Bot/DeploymentTemplates/DeployUseExistResourceGroup/readme.md
deleted file mode 100644
index 628f0a95..00000000
--- a/MultilingualBotSample/Bot/DeploymentTemplates/DeployUseExistResourceGroup/readme.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Usage
-The BotApp must be deployed prior to AzureBot.
-
-Command line:
-- az login
-- az deployment group create --resource-group --template-file --parameters @
-
-# parameters-for-template-BotApp-with-rg:
-
-- **appServiceName**:(required) The Name of the Bot App Service.
-
-- (choose an existingAppServicePlan or create a new AppServicePlan)
- - **existingAppServicePlanName**: The name of the App Service Plan.
- - **existingAppServicePlanLocation**: The location of the App Service Plan.
- - **newAppServicePlanName**: The name of the App Service Plan.
- - **newAppServicePlanLocation**: The location of the App Service Plan.
- - **newAppServicePlanSku**: The SKU of the App Service Plan. Defaults to Standard values.
-
-- **appType**: Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. **Allowed values are: MultiTenant(default), SingleTenant, UserAssignedMSI.**
-
-- **appId**:(required) Active Directory App ID or User-Assigned Managed Identity Client ID, set as MicrosoftAppId in the Web App's Application Settings.
-
-- **appSecret**:(required for MultiTenant and SingleTenant) Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings.
-
-- **UMSIName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource used for the Bot's Authentication.
-
-- **UMSIResourceGroupName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource Group used for the Bot's Authentication.
-
-- **tenantId**: The Azure AD Tenant ID to use as part of the Bot's Authentication. Only used for SingleTenant and UserAssignedMSI app types. Defaults to .
-
-MoreInfo: https://docs.microsoft.com/en-us/azure/bot-service/tutorial-provision-a-bot?view=azure-bot-service-4.0&tabs=userassigned%2Cnewgroup#create-an-identity-resource
-
-
-
-# parameters-for-template-AzureBot-with-rg:
-
-- **azureBotId**:(required) The globally unique and immutable bot ID.
-- **azureBotSku**: The pricing tier of the Bot Service Registration. **Allowed values are: F0, S1(default)**.
-- **azureBotRegion**: Specifies the location of the new AzureBot. **Allowed values are: global(default), westeurope**.
-- **botEndpoint**: Use to handle client messages, Such as https://.azurewebsites.net/api/messages.
-
-- **appType**: Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. **Allowed values are: MultiTenant(default), SingleTenant, UserAssignedMSI.**
-- **appId**:(required) Active Directory App ID or User-Assigned Managed Identity Client ID, set as MicrosoftAppId in the Web App's Application Settings.
-- **UMSIName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource used for the Bot's Authentication.
-- **UMSIResourceGroupName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource Group used for the Bot's Authentication.
-- **tenantId**: The Azure AD Tenant ID to use as part of the Bot's Authentication. Only used for SingleTenant and UserAssignedMSI app types. Defaults to .
-
-MoreInfo: https://docs.microsoft.com/en-us/azure/bot-service/tutorial-provision-a-bot?view=azure-bot-service-4.0&tabs=userassigned%2Cnewgroup#create-an-identity-resource
\ No newline at end of file
diff --git a/MultilingualBotSample/Bot/DeploymentTemplates/DeployWithNewResourceGroup/readme.md b/MultilingualBotSample/Bot/DeploymentTemplates/DeployWithNewResourceGroup/readme.md
deleted file mode 100644
index 23bf7a5a..00000000
--- a/MultilingualBotSample/Bot/DeploymentTemplates/DeployWithNewResourceGroup/readme.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# Usage
-The BotApp must be deployed prior to AzureBot.
-
-Command line:
-- az login
-- az deployment sub create --template-file --location --parameters @
-
-# parameters-for-template-BotApp-new-rg:
-
-- **groupName**:(required) Specifies the name of the new Resource Group.
-- **groupLocation**:(required) Specifies the location of the new Resource Group.
-
-- **appServiceName**:(required) The location of the App Service Plan.
-- **appServicePlanName**:(required) The name of the App Service Plan.
-- **appServicePlanLocation**: The location of the App Service Plan. Defaults to use groupLocation.
-- **appServicePlanSku**: The SKU of the App Service Plan. Defaults to Standard values.
-
-- **appType**: Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. **Allowed values are: MultiTenant(default), SingleTenant, UserAssignedMSI.**
-- **appId**:(required) Active Directory App ID or User-Assigned Managed Identity Client ID, set as MicrosoftAppId in the Web App's Application Settings.
-- **appSecret**:(required for MultiTenant and SingleTenant) Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings.
-- **UMSIName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource used for the Bot's Authentication.
-- **UMSIResourceGroupName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource Group used for the Bot's Authentication.
-- **tenantId**: The Azure AD Tenant ID to use as part of the Bot's Authentication. Only used for SingleTenant and UserAssignedMSI app types. Defaults to .
-
-MoreInfo: https://docs.microsoft.com/en-us/azure/bot-service/tutorial-provision-a-bot?view=azure-bot-service-4.0&tabs=userassigned%2Cnewgroup#create-an-identity-resource
-
-
-
-# parameters-for-template-AzureBot-new-rg:
-
-- **groupName**:(required) Specifies the name of the new Resource Group.
-- **groupLocation**:(required) Specifies the location of the new Resource Group.
-
-- **azureBotId**:(required) The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable.
-- **azureBotSku**: The pricing tier of the Bot Service Registration. **Allowed values are: F0, S1(default)**.
-- **azureBotRegion**: Specifies the location of the new AzureBot. **Allowed values are: global(default), westeurope**.
-- **botEndpoint**: Use to handle client messages, Such as https://.azurewebsites.net/api/messages.
-
-- **appType**: Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. **Allowed values are: MultiTenant(default), SingleTenant, UserAssignedMSI.**
-- **appId**:(required) Active Directory App ID or User-Assigned Managed Identity Client ID, set as MicrosoftAppId in the Web App's Application Settings.
-- **UMSIName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource used for the Bot's Authentication.
-- **UMSIResourceGroupName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource Group used for the Bot's Authentication.
-- **tenantId**: The Azure AD Tenant ID to use as part of the Bot's Authentication. Only used for SingleTenant and UserAssignedMSI app types. Defaults to .
-
-MoreInfo: https://docs.microsoft.com/en-us/azure/bot-service/tutorial-provision-a-bot?view=azure-bot-service-4.0&tabs=userassigned%2Cnewgroup#create-an-identity-resource
\ No newline at end of file
diff --git a/MultilingualBotSample/Bot/README.md b/MultilingualBotSample/Bot/README.md
deleted file mode 100644
index 44e9f68d..00000000
--- a/MultilingualBotSample/Bot/README.md
+++ /dev/null
@@ -1,69 +0,0 @@
-# TranslationBot
-
-Bot Framework v4 empty bot sample.
-
-This bot has been created using [Bot Framework](https://dev.botframework.com), it shows the minimum code required to build a bot.
-
-## Prerequisites
-
-- [.NET SDK](https://dotnet.microsoft.com/download) version 6.0
-
- ```bash
- # determine dotnet version
- dotnet --version
- ```
-
-## To try this sample
-
-- In a terminal, navigate to `TranslationBot`
-
- ```bash
- # change into project folder
- cd TranslationBot
- ```
-
-- Run the bot from a terminal or from Visual Studio, choose option A or B.
-
- A) From a terminal
-
- ```bash
- # run the bot
- dotnet run
- ```
-
- B) Or from Visual Studio
-
- - Launch Visual Studio
- - File -> Open -> Project/Solution
- - Navigate to `TranslationBot` folder
- - Select `TranslationBot.csproj` file
- - Press `F5` to run the project
-
-## Testing the bot using Bot Framework Emulator
-
-[Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel.
-
-- Install the Bot Framework Emulator version 4.3.0 or greater from [here](https://github.com/Microsoft/BotFramework-Emulator/releases)
-
-### Connect to the bot using Bot Framework Emulator
-
-- Launch Bot Framework Emulator
-- File -> Open Bot
-- Enter a Bot URL of `http://localhost:3978/api/messages`
-
-## Deploy the bot to Azure
-
-To learn more about deploying a bot to Azure, see [Deploy your bot to Azure](https://aka.ms/azuredeployment) for a complete list of deployment instructions.
-
-## Further reading
-
-- [Bot Framework Documentation](https://docs.botframework.com)
-- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
-- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
-- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
-- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0)
-- [.NET Core CLI tools](https://docs.microsoft.com/en-us/dotnet/core/tools/?tabs=netcore2x)
-- [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest)
-- [Azure Portal](https://portal.azure.com)
-- [Language Understanding using LUIS](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/)
-- [Channels and Bot Connector Service](https://docs.microsoft.com/en-us/azure/bot-service/bot-concepts?view=azure-bot-service-4.0)
diff --git a/MultilingualBotSample/README.md b/MultilingualBotSample/README.md
deleted file mode 100644
index f62beeb6..00000000
--- a/MultilingualBotSample/README.md
+++ /dev/null
@@ -1,443 +0,0 @@
-# Translation Bot sample
-
-## Overview
-The main idea of this sample is to show the user how a PVA Bot can be connected using DirecLine API and using all its topics in different languages, by using a middleware (Azure Bot) to translate the messages between the user and the PVA Bot. The middleware will be using Cognitive services to translate the texts during the entire conversation.
-
-## Prerequisites
-
-- An Azure subscription
-- A Translator service deployed on Azure
-- A custom dictionary already published (optional)
-
-Follow this [link](https://docs.microsoft.com/en-us/azure/cognitive-services/translator/translator-how-to-signup) to have more information on how to create the Translator service.
-
-## Bot resources list
-These are the Bot resources needed by the sample:
-- An Azure Bot (middleware)
-- A PVA Bot
-
-## Features
-The sample supports the following features:
-- Basic text translation
-- Adaptive cards and Flows translations
-- Custom dictionaries
-- To add Omnichannel integration please check the additional readme file.
-
-## Architecture diagram
-
-
-## How the bot works
-1. The user sends a message to the PVA bot
-2. The middleware (Azure Bot) intercepts the message and translates it (if needed) to the PVA Bot's language before sending it
-4. The PVA bot will receive the message and trigger a topic based on the user's message
-3. The PVA bot response is sent back to the user
-4. The middleware intercepts and translates the message back (if needed) based on the user's language
-7. The user gets the message
-
-## Create a Translator resource
-### Create your resource
-Azure Translator is a cloud-based machine translation service that is part of the Azure Cognitive Services family of REST APIs. Azure resources are instances of services that you create on the [Azure portal](https://portal.azure.com/#create/Microsoft.CognitiveServicesTextTranslation).
-
-### Complete your project and instance details
-1. Subscription: Select one of your available Azure subscriptions.
-
-2. Resource Group: You can create a new resource group or add your resource to a pre-existing resource group that shares the same lifecycle, permissions, and policies.
-
-3. Resource Region: Choose Global unless your business or application requires a specific region. If you're planning on using the Document Translation feature with managed identity authentication, choose a non-global region.
-
-4. Name: Enter the name you have chosen for your resource. The name you choose must be unique within Azure.
-
-5. Pricing tier: Select a pricing tier that meets your needs:
-
- - Each subscription has a free tier.
- - The free tier has the same features and functionality as the paid plans and doesn't expire.
- - Only one free tier is available per subscription.
- - Document Translation isn't supported in the free tier. Select Standard S1 to try that feature.
-
-6. If you've created a multi-service resource, you'll need to confirm additional usage details via the checkboxes.
-
-7. Select Review + Create.
-
-8. Review the service terms and select Create to deploy the resource.
-
-9. After your resource has successfully deployed, select Go to the resource.
-
-### Authentication keys and endpoint URL
-All Cognitive Services API requests require an endpoint URL and a read-only key for authentication
-
-- Authentication keys. Your key is a unique string that is passed on every request to the Translation service. You can pass your key through a query-string parameter or by specifying it in the HTTP request header.
-
-- Endpoint URL. Use the Global endpoint in your API request unless you need a specific Azure region or custom endpoint. See Base URLs. The Global endpoint URL is api.cognitive.microsofttranslator.com.
-
-### Get your authentication keys and endpoint
-1. After your new resource deploys, select Go to resource or navigate directly to your resource page.
-2. In the left rail, under Resource Management, select Keys and Endpoint.
-3. Copy and paste your key and region in a convenient location, such as Microsoft Notepad.
-
-
-## Create an App registration
-
-An App registration is needed to deploy an Azure Bot. The following sections will give more details on this task.
-
-### Permissions required for registering an App
-
-The user needs to have sufficient permissions to register an App in the Azure AD tenant. Check this [link](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal#permissions-required-for-registering-an-app) for more information about it.
-
-### Steps to create an App registration
-
-1. Sign in to the [Azure Portal](https://portal.azure.com/).
-2. If you have access to multiple tenants, use the Directories + subscriptions filter in the top menu to switch to the tenant in which you want to register the application.
-3. Search for and select Azure Active Directory.
-4. Under Manage, select App registrations > New registration.
-5. Enter a display name for the application. Users of this application might see the display name when they use the app. The app registration's automatically generated Application (client) ID, not its display name, uniquely identifies the app within the identity platform.
-6. Specify the account type, by selecting the option "Accounts in any organizational directory and personal Microsoft accounts".
-7. Don't enter anything for Redirect URI (optional).
-8. Select Register to complete the initial app registration.
-9. From the overview page, copy the Application (client) Id and the Tenant id values as they will be used in further steps.
-
-### Add a credential to the App registration
-
-Credentials allow an application to authenticate as a user, requiring no interaction from a user at runtime.
-
-1. In the Azure portal, in App registrations, select the application created earlier.
-2. Select Certificates & secrets > Client secrets > New client secret.
-3. Add a secret description.
-4. Select an expiration for the secret or specify a custom lifetime.
-5. Select Add.
-6. Record the secret's value. This secret value is never displayed again after you leave this page.
-
-## Bot deployment
-
-The bot can be deployed using these two methods:
-- Using ARM templates
-- using an Azure Pipeline
-
-### Deploy the bot using ARM templates
-
-Follow these steps to deploy the bot using the Azure CLI. This approach assumes that the resource group already exists.
-
-1. Download the Azure CLI (if needed) from this [link](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli-windows?tabs=azure-cli).
-
-2. Download the Zip file containing the Bot source code from this repo.
-
-3. Open the appsettings.json file located in the Bot folder. The file should look like this image.
-
- 
-
- Update the following settings, ommiting the optional ones:
-
- - TokenEndpoint: https://powerva.microsoft.com/api/botmanagement/v1/directline/directlinetoken
- - BotId: The id of the PVA bot.
- ```
- NOTE: To obtain the bot id go to Settings -> Channels -> Mobile App and copy the token endpoint where you will find the bot id
- ```
-
- 
-
- - BotName: The name of the PVA bot.
- - TenantId: Bot Tenant id. This can be obtained from the details page on PVA under the manage option.
-
- 
-
- - TranslatorKey: The value in the Azure secret key for the Translator resource.
- - TranslatorRegion: The region of the multi-service or regional translator resource.
- - TranslatorCategoryId: The category id for the translator service's custom dictionary (optional).
- For example:
- ```
- "TranslatorCategoryId": {
- "en": {
- "dictionary": "category-id"
- }
- }
- ```
- - BotLanguage: your PVA bot's language (e.g: en).
- - DetectLanguageOnce: true/false value to enable language detection on the first message or every message received from the user.
- - GetLanguageFromUri: true/false value to get the language to be used in the conversation. It will be received through the connection endpoint (optional).
- For example:
- ```
- http://localhost:3979/api/messages/es
- ```
- - PVATopicExceptionTag: Tag to be used as an exception to avoid the translation of user's responses (optional).
- - EscalationPhrases: Phrases that will be used to identify and handle the hand-off process to a human agent in Omnichannel (optional).
- - MicrosoftAppId: App id obtained from the App registration Overview page.
-
- 
-
- - MicrosoftAppPassword: Secret configured for the App registration.
-
- 
-
-3. Open the CMD or Powershell console and login to Azure using the following command:
- ```
- az login
- ```
-
-4. Update the parameters-for-template-BotApp-with-rg.json file with the proper information, this file can be found in the Bot root folder under the DeploymentTemplates\DeployUseExistResourceGroup directory.
- The file should look like this image.
-
- 
-
- Update the following settings, ommiting the optional ones:
-
- - appServiceName: The globally unique name of the Web App.
- - existingAppServicePlanName (optional): The name of an existing appService if you have one, otherwise leave it empty.
- - existingAppServicePlanLocation (optional): The location of an existing appService if you have one, otherwise leave it empty.
- - newAppServicePlanName: The name of the new App Service Plan.
- - newAppServicePlanLocation: The location of the App Service Plan.
- - newAppServicePlanSku: The SKU of the App Service Plan. Defaults to Standard values.
- - appType: Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. Allowed values are MultiTenant, SingleTenant, UserAssignedMSI. Defaults to MultiTenant.
- - appId: App id obtained from the App registration Overview page.
- - appSecret: Secret configured for the App registration.
- - UMSIName (optional): For user-assigned managed identity app types, the name of the identity resource. Leave it empty.
- - UMSIResourceGroupName (optional): For user-assigned managed identity app types, the resource group for the identity resource. Leave it empty.
- - tenantId: Tenant id obtained from the App registration Overview page.
-
-5. Deploy the BotApp using the following command. These parameters need to be specified:
- - resource-group: The name of the resource group used to deploy the resources.
- - template-file: The path of the template-BotApp-with-rg.json file. This file can be found in the Bot root folder under the DeploymentTemplates\DeployUseExistResourceGroup directory.
- - parameters: The path of the parameters-for-template-BotApp-with-rg.json file This file can be found in the Bot root folder under the DeploymentTemplates\DeployUseExistResourceGroup directory.
- ```
- az deployment group create --resource-group "resource-group-name" --template-file template-BotApp-with-rg.json --parameters "parameters-for-template-BotApp-with-rg.json"
- ```
-
- Once executed a message like the following will be received:
-
- 
-
-6. Update the parameters-for-template-AzureBot-with-rg.json file with the proper information, this file can be found in the Bot root folder under the DeploymentTemplates\DeployUseExistResourceGroup directory.
- The file should look like this image.
-
- 
-
- Update the following settings, ommiting the optional ones:
-
- - azureBotId: The globally unique and immutable bot ID.
- - azureBotSku: The pricing tier of the Bot Service Registration.
- - azureBotRegion: Specifies the location of the new AzureBot.
- - botEndpoint: Use to handle client messages, Such as https://[botappServiceName].azurewebsites.net/api/messages.
- - appType: Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. Allowed values are MultiTenant, SingleTenant, UserAssignedMSI. Defaults to MultiTenant.
- - appId: App id obtained from the App registration Overview page.
- - UMSIName (optional): For user-assigned managed identity app types, the name of the identity resource. Leave it empty.
- - UMSIResourceGroupName (optional): For user-assigned managed identity app types, the resource group for the identity resource. Leave it empty.
- - tenantId: Tenant id obtained from the App registration Overview page.
-
-7. Deploy the AzureBot using the following command. These parameters need to be specified:
- - resource-group: The name of the resource group used to deploy the resources.
- - template-file: The path of the template-AzureBot-with-rg.json file. This file can be found in the Bot root folder under the DeploymentTemplates\DeployUseExistResourceGroup directory.
- - parameters: The path of the parameters-for-template-AzureBot-with-rg.json file This file can be found in the Bot root folder under the DeploymentTemplates\DeployUseExistResourceGroup directory.
- ```
- az deployment group create --resource-group "resource-group-name" --template-file template-AzureBot-with-rg.json --parameters "parameters-for-template-AzureBot-with-rg.json"
- ```
-
- Once executed a message like the following will be received:
-
- 
-
-8. Execute the following command:
- ```
- az bot prepare-deploy --lang Csharp --code-dir "." --proj-file-path ""
- ```
-
- Once executed a message like the following will be received:
-
- 
-
-9. Create a zip file with the solution content. This zip file should contain all the folders and files included in the Bot directory.
-
- 
-
-10. Execute the deployment command. These parameters need to be specified:
- - resource-group: The name of the resource group used to deploy the resources.
- - name: App service name.
- - src: Path to the zip file created in the previous step.
- ```
- az webapp deployment source config-zip --resource-group "resource-group-name" --name "app-service-name" --src "zip-file-name"
- ```
-
- Once executed a message like the following will be received:
-
- 
-
-11. Now the bot and its components are deployed, so you can move on to the "How to use the bot" section.
-
-### Deploy the bot using Azure pipeline
-
-### Step 1: Create an Azure Resource Manager service connection
-
-1. In Azure DevOps, open the Service connections page from the [project settings page](https://docs.microsoft.com/en-us/azure/devops/project/navigation/go-to-service-page?view=azure-devops#open-project-settings). In TFS, open the Services page from the "settings" icon in the top menu bar.
-
-2. Choose + New service connection and select Azure Resource Manager.
-
- 
-
-3. Choose Service Principal (manual) option and enter the Service Principal details.
-
- 
-
-4. Enter a user-friendly Connection name to use when referring to this service connection.
-
-5. Select the Environment name (such as Azure Cloud, Azure Stack, or an Azure Government Cloud).
-
-6. If you do not select Azure Cloud, enter the Environment URL. For Azure Stack, this will be something like https://management.local.azurestack.external
-
-7. Select the Scope level you require:
-
- - If you choose Subscription, select an existing Azure subscription. If you don't see any Azure subscriptions or instances, see [Troubleshoot Azure Resource Manager service connections](https://docs.microsoft.com/en-us/azure/devops/pipelines/release/azure-rm-endpoint?view=azure-devops).
- - If you choose Management Group, select an existing Azure management group. See [Create management groups](https://docs.microsoft.com/en-us/azure/azure-resource-manager/management-groups-create).
-
-8. Enter the information about your service principal into the Azure subscription dialog textboxes:
-
- - Subscription ID
- - Subscription name
- - Service principal ID
- - Either the service principal client key or, if you have selected Certificate, enter the contents of both the certificate and private key sections of the *.pem file.
- - Tenant ID
-
- You can obtain this information if you don't have it by hand downloading and running [this PowerShell script](https://github.com/Microsoft/vsts-rm-extensions/blob/master/TaskModules/powershell/Azure/SPNCreation.ps1) in an Azure PowerShell window. When prompted, enter your subscription name, password, role (optional), and the type of cloud such as Azure Cloud (the default), Azure Stack, or an Azure Government Cloud.
-
-9. Choose Verify connection to validate the settings you've entered.
-
-10. After the new service connection is created:
-
- - If you are using it in the UI, select the connection name you assigned in the Azure subscription setting of your pipeline.
- - If you are using it in YAML, copy the connection name into your code as the azureSubscription value.
-
-11. If required, modify the service principal to expose the appropriate permissions. For more details, see [Use Role-Based Access Control to manage access to your Azure subscription resources](https://docs.microsoft.com/en-us/azure/role-based-access-control/role-assignments-portal). [This blog post](https://devblogs.microsoft.com/devops/automating-azure-resource-group-deployment-using-a-service-principal-in-visual-studio-online-buildrelease-management/) also contains more information about using service principal authentication.
-
-### Step 2: Create the Azure pipeline
-
-1. Login to your [Azure dev](https://dev.azure.com/) account
-
-2. On the left menu click on pipelines -> pipelines
-
- 
-
-3. Select New pipeline
-
-4. On the connect window, select the GitHub option to connect the pipeline to your repository
-
- 
-
-5. Select your repository
-
-6. On the configure your pipeline window select the Existing Azure Pipelines YAML file option
-
- 
-
-7. Select the branch and path where your YAML file is located inside your repository
-
- 
-
-8. On the top right corner, click on Save
-
- 
-
-
-9. Complete the commit message, description, select your branch and click on save to commit the changes to your repository.
-
- 
-
-10. On the top right corner, click on variables and setup the following variables:
-
- 
-
- Note: Make sure to check the "Let users override this value when running this pipeline" box on every variable
-
- 
-
- - TokenEndpoint: https://powerva.microsoft.com/api/botmanagement/v1/directline/directlinetoken
- - BotId: The id of the PVA bot.
- - BotName: The name of the PVA bot.
- - TenantId: Bot Tenant id. This can be obtained from the details page on PVA under the manage option.
- - TranslatorKey: The value in the Azure secret key for the Translator resource.
- - TranslatorRegion: The region of the multi-service or regional translator resource.
- - TranslatorCategoryId: The category id for the translator service's custom dictionary (optional).
- For example:
- ```
- "TranslatorCategoryId": {
- "en": {
- "dictionary": "category-id"
- }
- }
- ```
- - BotLanguage: your PVA bot's language (e.g: en).
- - DetectLanguageOnce: true/false value to enable language detection on the first message or every message received from the user.
- - GetLanguageFromUri: true/false value to get the language to be used in the conversation. It will be received through the connection endpoint (optional).
- For example:
- ```
- http://localhost:3979/api/messages/es
- ```
- - PVATopicExceptionTag: Tag to be used as an exception to avoid the translation of user's responses (optional).
- - EscalationPhrases: Phrases that will be used to identify and handle the hand-off process to a human agent in Omnichannel (optional).
-
- Note: For the last variables, make sure to also check the "Keep this value secret" box.
-
- 
-
- - MicrosoftAppId: App id obtained from the App registration Overview page.
- - MicrosoftAppPassword: Secret configured for the App registration.
-
-11. On the top right corner, click on Run
-
- 
-
-12. Select the Branch/tag and commit where the pipeline version you want to run is located
-
- 
-
-13. Now the bot and its components are deployed, so you can move on to the next section.
-
-## How to use the bot
-
-After the bot is deployed it can be tested using one of the following methods:
-
-### Bot Framework Emulator
-1. Install the Bot Framework Emulator in case it is not already installed from [this](https://github.com/Microsoft/BotFramework-Emulator/blob/master/README.md) link. Here is another [link](https://learn.microsoft.com/en-us/azure/bot-service/bot-service-debug-emulator?view=azure-bot-service-4.0&tabs=csharp) with additional information on how to use the Bot Framework Emulator tool.
-2. Get the messaging endpoint of your bot by going to the [Azure Portal](https://portal.azure.com) and clicking on the Azure Bot resource
-
- 
-
-3. Connect to your bot on Bot Framework Emulator by specifying your messaging endpoint, the AppID and AppPassword you used to deploy your bot
-
-4. Enter one of the triggering phrases in order to start the conversation with your PVA bot in the desired language
-
-### Azure portal
-1. Go to the [Azure Portal](https://portal.azure.com) and select your Azure bot
-2. On the left panel, click on Test in Web Chat under settings
-
- 
-
-## Troubleshoot using appsettings.json
-If the Bot is not working properly after the deployment, the values of the deployed appsettings can be validated from the Azure Portal instead of doing it locally and deploying again. To do this, follow these steps:
-
-1. Go to the [Azure Portal](https://portal.azure.com) and select your Azure bot webapp.
-2. On the left panel, under development tools, click on App Service Editor and then click on open editor.
-
- 
-
-3. A new window will be opened with the appsettings and all files.
-
- 
-
-4. These settings will be saved automatically.
-
-## Setting up exceptions in the translation Bot (optional)
-Exceptions can be configured to avoid the translation of a particular user's response to a Bot question. This can be achieved using a custom PVA topic.
-
-### Using a custom PVA topic
-This scenario will enable Bot authors using PVA to set up a flag topic that will be used in other topics to indicate that the response to a particular question should not be translated.
-Steps to configure:
-
-1. Create a new topic that will be used to flag the exception.
-
- 
-
-2. Edit the topic where the exception should be applied. The exception topic should be invoked before the question that will be sent to the user.
-
- 
-
-3. Edit the Bot appsettings file to configure the tag used for the topic created in step 1.
-
- ```
- "PVATopicExceptionTag": "#DONOTTRANSLATE#",
- ```
diff --git a/PVA Architecture Series/PVA_Arch_Bot_1_0_0_1.zip b/PVA Architecture Series/PVA_Arch_Bot_1_0_0_1.zip
deleted file mode 100644
index 7da2228e..00000000
Binary files a/PVA Architecture Series/PVA_Arch_Bot_1_0_0_1.zip and /dev/null differ
diff --git a/PVA Architecture Series/PVA_Arch_Series.pptx b/PVA Architecture Series/PVA_Arch_Series.pptx
deleted file mode 100644
index 9ba29776..00000000
Binary files a/PVA Architecture Series/PVA_Arch_Series.pptx and /dev/null differ
diff --git a/PVA Architecture Series/img/PVA_Arch_Title.png b/PVA Architecture Series/img/PVA_Arch_Title.png
deleted file mode 100644
index 17d72c4d..00000000
Binary files a/PVA Architecture Series/img/PVA_Arch_Title.png and /dev/null differ
diff --git a/PVA Architecture Series/readme.md b/PVA Architecture Series/readme.md
deleted file mode 100644
index 427d57fb..00000000
--- a/PVA Architecture Series/readme.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Supporting content for the Power Virtual Agents architecture series
-
-
-
-Supplementary materials for the Power Virtual Agents Architecture series. View the series:
-=======
-
-## File
-
-- PVA_Arch_Series.pptx - this is the PowerPoint deck we use in the series - all six sessions.
-- PVA_Arch_Bot_1_0_01.zip - This is a solution export that you can use to understand PVA, and as the basis for a new bot. We show this bot in the series - you can import into your environment and fllow along.
-
-## Installation
-
-To set up ya bot using the solution example above, you will need to have access to a Power Platform Environment (a Trial or Sandbox environment is fine). To import, please follow the documented instructions: https://docs.microsoft.com/en-us/power-virtual-agents/authoring-export-import-bots#export-and-import-bots
\ No newline at end of file
diff --git a/PVATestFramework/Images/add_a_client_secret.png b/PVATestFramework/Images/add_a_client_secret.png
deleted file mode 100644
index dda9da2b..00000000
Binary files a/PVATestFramework/Images/add_a_client_secret.png and /dev/null differ
diff --git a/PVATestFramework/Images/add_app.png b/PVATestFramework/Images/add_app.png
deleted file mode 100644
index fe322435..00000000
Binary files a/PVATestFramework/Images/add_app.png and /dev/null differ
diff --git a/PVATestFramework/Images/api_permissions.png b/PVATestFramework/Images/api_permissions.png
deleted file mode 100644
index 7339fc58..00000000
Binary files a/PVATestFramework/Images/api_permissions.png and /dev/null differ
diff --git a/PVATestFramework/Images/architecture-diagram.png b/PVATestFramework/Images/architecture-diagram.png
deleted file mode 100644
index 4ac04d8b..00000000
Binary files a/PVATestFramework/Images/architecture-diagram.png and /dev/null differ
diff --git a/PVATestFramework/Images/azure.png b/PVATestFramework/Images/azure.png
deleted file mode 100644
index e342d376..00000000
Binary files a/PVATestFramework/Images/azure.png and /dev/null differ
diff --git a/PVATestFramework/Images/bash.png b/PVATestFramework/Images/bash.png
deleted file mode 100644
index bd3980fa..00000000
Binary files a/PVATestFramework/Images/bash.png and /dev/null differ
diff --git a/PVATestFramework/Images/chat.png b/PVATestFramework/Images/chat.png
deleted file mode 100644
index 99be6da4..00000000
Binary files a/PVATestFramework/Images/chat.png and /dev/null differ
diff --git a/PVATestFramework/Images/chat2.png b/PVATestFramework/Images/chat2.png
deleted file mode 100644
index ca0adc46..00000000
Binary files a/PVATestFramework/Images/chat2.png and /dev/null differ
diff --git a/PVATestFramework/Images/chat3.png b/PVATestFramework/Images/chat3.png
deleted file mode 100644
index 9deace89..00000000
Binary files a/PVATestFramework/Images/chat3.png and /dev/null differ
diff --git a/PVATestFramework/Images/chat_regex.png b/PVATestFramework/Images/chat_regex.png
deleted file mode 100644
index 7823c673..00000000
Binary files a/PVATestFramework/Images/chat_regex.png and /dev/null differ
diff --git a/PVATestFramework/Images/chat_to_json.png b/PVATestFramework/Images/chat_to_json.png
deleted file mode 100644
index 4588c84d..00000000
Binary files a/PVATestFramework/Images/chat_to_json.png and /dev/null differ
diff --git a/PVATestFramework/Images/conversion.png b/PVATestFramework/Images/conversion.png
deleted file mode 100644
index f166b467..00000000
Binary files a/PVATestFramework/Images/conversion.png and /dev/null differ
diff --git a/PVATestFramework/Images/created_resource.png b/PVATestFramework/Images/created_resource.png
deleted file mode 100644
index 095fb09f..00000000
Binary files a/PVATestFramework/Images/created_resource.png and /dev/null differ
diff --git a/PVATestFramework/Images/execution.png b/PVATestFramework/Images/execution.png
deleted file mode 100644
index fbfc0207..00000000
Binary files a/PVATestFramework/Images/execution.png and /dev/null differ
diff --git a/PVATestFramework/Images/files.png b/PVATestFramework/Images/files.png
deleted file mode 100644
index 64418c3b..00000000
Binary files a/PVATestFramework/Images/files.png and /dev/null differ
diff --git a/PVATestFramework/Images/github-pipeline-new.png b/PVATestFramework/Images/github-pipeline-new.png
deleted file mode 100644
index ef9c8e2f..00000000
Binary files a/PVATestFramework/Images/github-pipeline-new.png and /dev/null differ
diff --git a/PVATestFramework/Images/github-pipeline-run.png b/PVATestFramework/Images/github-pipeline-run.png
deleted file mode 100644
index 56c1909e..00000000
Binary files a/PVATestFramework/Images/github-pipeline-run.png and /dev/null differ
diff --git a/PVATestFramework/Images/github-pipeline-yourself.png b/PVATestFramework/Images/github-pipeline-yourself.png
deleted file mode 100644
index 01f53fb2..00000000
Binary files a/PVATestFramework/Images/github-pipeline-yourself.png and /dev/null differ
diff --git a/PVATestFramework/Images/input_output.png b/PVATestFramework/Images/input_output.png
deleted file mode 100644
index 2e567f9c..00000000
Binary files a/PVATestFramework/Images/input_output.png and /dev/null differ
diff --git a/PVATestFramework/Images/log.png b/PVATestFramework/Images/log.png
deleted file mode 100644
index 1953a55d..00000000
Binary files a/PVATestFramework/Images/log.png and /dev/null differ
diff --git a/PVATestFramework/Images/new_app_user.png b/PVATestFramework/Images/new_app_user.png
deleted file mode 100644
index b7dd9887..00000000
Binary files a/PVATestFramework/Images/new_app_user.png and /dev/null differ
diff --git a/PVATestFramework/Images/new_client_secret.png b/PVATestFramework/Images/new_client_secret.png
deleted file mode 100644
index 2ea424e6..00000000
Binary files a/PVATestFramework/Images/new_client_secret.png and /dev/null differ
diff --git a/PVATestFramework/Images/new_registration.png b/PVATestFramework/Images/new_registration.png
deleted file mode 100644
index ee7ff768..00000000
Binary files a/PVATestFramework/Images/new_registration.png and /dev/null differ
diff --git a/PVATestFramework/Images/pattern.png b/PVATestFramework/Images/pattern.png
deleted file mode 100644
index 31c6f9b3..00000000
Binary files a/PVATestFramework/Images/pattern.png and /dev/null differ
diff --git a/PVATestFramework/Images/pattern1.png b/PVATestFramework/Images/pattern1.png
deleted file mode 100644
index 1ff0dce9..00000000
Binary files a/PVATestFramework/Images/pattern1.png and /dev/null differ
diff --git a/PVATestFramework/Images/pattern2.png b/PVATestFramework/Images/pattern2.png
deleted file mode 100644
index d92ffa36..00000000
Binary files a/PVATestFramework/Images/pattern2.png and /dev/null differ
diff --git a/PVATestFramework/Images/pipeline-linux.png b/PVATestFramework/Images/pipeline-linux.png
deleted file mode 100644
index 9a1a5476..00000000
Binary files a/PVATestFramework/Images/pipeline-linux.png and /dev/null differ
diff --git a/PVATestFramework/Images/pipeline1.png b/PVATestFramework/Images/pipeline1.png
deleted file mode 100644
index 9adf33b6..00000000
Binary files a/PVATestFramework/Images/pipeline1.png and /dev/null differ
diff --git a/PVATestFramework/Images/pipeline2.png b/PVATestFramework/Images/pipeline2.png
deleted file mode 100644
index 21801c2a..00000000
Binary files a/PVATestFramework/Images/pipeline2.png and /dev/null differ
diff --git a/PVATestFramework/Images/pipeline3.png b/PVATestFramework/Images/pipeline3.png
deleted file mode 100644
index 01e484a9..00000000
Binary files a/PVATestFramework/Images/pipeline3.png and /dev/null differ
diff --git a/PVATestFramework/Images/pipeline_bash.png b/PVATestFramework/Images/pipeline_bash.png
deleted file mode 100644
index f5c0412f..00000000
Binary files a/PVATestFramework/Images/pipeline_bash.png and /dev/null differ
diff --git a/PVATestFramework/Images/power_apps.png b/PVATestFramework/Images/power_apps.png
deleted file mode 100644
index 25e68341..00000000
Binary files a/PVATestFramework/Images/power_apps.png and /dev/null differ
diff --git a/PVATestFramework/Images/power_platform.png b/PVATestFramework/Images/power_platform.png
deleted file mode 100644
index 3df2ad49..00000000
Binary files a/PVATestFramework/Images/power_platform.png and /dev/null differ
diff --git a/PVATestFramework/Images/powerapps_session_details.png b/PVATestFramework/Images/powerapps_session_details.png
deleted file mode 100644
index 620cd1ab..00000000
Binary files a/PVATestFramework/Images/powerapps_session_details.png and /dev/null differ
diff --git a/PVATestFramework/Images/powerapps_settings.png b/PVATestFramework/Images/powerapps_settings.png
deleted file mode 100644
index d1b58534..00000000
Binary files a/PVATestFramework/Images/powerapps_settings.png and /dev/null differ
diff --git a/PVATestFramework/Images/publish_linux.png b/PVATestFramework/Images/publish_linux.png
deleted file mode 100644
index 044bd24f..00000000
Binary files a/PVATestFramework/Images/publish_linux.png and /dev/null differ
diff --git a/PVATestFramework/Images/publish_selection.png b/PVATestFramework/Images/publish_selection.png
deleted file mode 100644
index 0a119893..00000000
Binary files a/PVATestFramework/Images/publish_selection.png and /dev/null differ
diff --git a/PVATestFramework/Images/publish_settings.png b/PVATestFramework/Images/publish_settings.png
deleted file mode 100644
index e3c3cd1b..00000000
Binary files a/PVATestFramework/Images/publish_settings.png and /dev/null differ
diff --git a/PVATestFramework/Images/publish_settings2.png b/PVATestFramework/Images/publish_settings2.png
deleted file mode 100644
index 754e11cb..00000000
Binary files a/PVATestFramework/Images/publish_settings2.png and /dev/null differ
diff --git a/PVATestFramework/Images/publish_target.png b/PVATestFramework/Images/publish_target.png
deleted file mode 100644
index c616ba6f..00000000
Binary files a/PVATestFramework/Images/publish_target.png and /dev/null differ
diff --git a/PVATestFramework/Images/pva-token-1.png b/PVATestFramework/Images/pva-token-1.png
deleted file mode 100644
index 63326981..00000000
Binary files a/PVATestFramework/Images/pva-token-1.png and /dev/null differ
diff --git a/PVATestFramework/Images/pva-token-2.png b/PVATestFramework/Images/pva-token-2.png
deleted file mode 100644
index e50cbef7..00000000
Binary files a/PVATestFramework/Images/pva-token-2.png and /dev/null differ
diff --git a/PVATestFramework/Images/pva-token-3.png b/PVATestFramework/Images/pva-token-3.png
deleted file mode 100644
index ef122254..00000000
Binary files a/PVATestFramework/Images/pva-token-3.png and /dev/null differ
diff --git a/PVATestFramework/Images/regional.png b/PVATestFramework/Images/regional.png
deleted file mode 100644
index 5af88e6c..00000000
Binary files a/PVATestFramework/Images/regional.png and /dev/null differ
diff --git a/PVATestFramework/Images/register.png b/PVATestFramework/Images/register.png
deleted file mode 100644
index 916d606a..00000000
Binary files a/PVATestFramework/Images/register.png and /dev/null differ
diff --git a/PVATestFramework/Images/request_api_permissions.png b/PVATestFramework/Images/request_api_permissions.png
deleted file mode 100644
index b1f3abb3..00000000
Binary files a/PVATestFramework/Images/request_api_permissions.png and /dev/null differ
diff --git a/PVATestFramework/Images/run_tests.png b/PVATestFramework/Images/run_tests.png
deleted file mode 100644
index b6dd10a5..00000000
Binary files a/PVATestFramework/Images/run_tests.png and /dev/null differ
diff --git a/PVATestFramework/Images/secret.png b/PVATestFramework/Images/secret.png
deleted file mode 100644
index c62e50a5..00000000
Binary files a/PVATestFramework/Images/secret.png and /dev/null differ
diff --git a/PVATestFramework/Images/security_roles.png b/PVATestFramework/Images/security_roles.png
deleted file mode 100644
index 320b0cfc..00000000
Binary files a/PVATestFramework/Images/security_roles.png and /dev/null differ
diff --git a/PVATestFramework/Images/select_app.png b/PVATestFramework/Images/select_app.png
deleted file mode 100644
index b1938fbf..00000000
Binary files a/PVATestFramework/Images/select_app.png and /dev/null differ
diff --git a/PVATestFramework/Images/service_reader.png b/PVATestFramework/Images/service_reader.png
deleted file mode 100644
index 2944bdb4..00000000
Binary files a/PVATestFramework/Images/service_reader.png and /dev/null differ
diff --git a/PVATestFramework/Images/single_file.png b/PVATestFramework/Images/single_file.png
deleted file mode 100644
index 01136285..00000000
Binary files a/PVATestFramework/Images/single_file.png and /dev/null differ
diff --git a/PVATestFramework/Images/single_file_linux.png b/PVATestFramework/Images/single_file_linux.png
deleted file mode 100644
index 713852da..00000000
Binary files a/PVATestFramework/Images/single_file_linux.png and /dev/null differ
diff --git a/PVATestFramework/Images/user_impersonation.png b/PVATestFramework/Images/user_impersonation.png
deleted file mode 100644
index e094d4ff..00000000
Binary files a/PVATestFramework/Images/user_impersonation.png and /dev/null differ
diff --git a/PVATestFramework/Images/user_permissions.png b/PVATestFramework/Images/user_permissions.png
deleted file mode 100644
index 4c321498..00000000
Binary files a/PVATestFramework/Images/user_permissions.png and /dev/null differ
diff --git a/PVATestFramework/Images/variables_wiz.png b/PVATestFramework/Images/variables_wiz.png
deleted file mode 100644
index 39a194e1..00000000
Binary files a/PVATestFramework/Images/variables_wiz.png and /dev/null differ
diff --git a/PVATestFramework/Images/variables_wiz2.png b/PVATestFramework/Images/variables_wiz2.png
deleted file mode 100644
index a1ddc666..00000000
Binary files a/PVATestFramework/Images/variables_wiz2.png and /dev/null differ
diff --git a/PVATestFramework/PUBLISH.MD b/PVATestFramework/PUBLISH.MD
deleted file mode 100644
index a998b755..00000000
--- a/PVATestFramework/PUBLISH.MD
+++ /dev/null
@@ -1,51 +0,0 @@
-# How to publish the solution on Windows
-To use the tool inside a pipeline, a single .exe file must be generated with the solution.
-This guide will show you the steps for doing this:
-
-1. from Visual Studio with the solution opened go to Build -> Publish Selection.
-
-
-
-2. a new tab will open where you can select the target location, framework and runtime.
-
-
-
-3. from there click on "Show all settings", and in the "File publish options" click on the "Produce single file" checkbox.
-
-
-
-4. after that, click on "Save" and then "Publish"
-
-
-
-5. you can open the Target location by clicking on either the "Open folder" link or the path.
-
-6. now you have the solution on a single .exe file.
-
-
-
-# How to publish the solution on Linux
-To use the tool inside a pipeline, a single file must be generated with the solution.
-This guide will show you the steps for doing this:
-
-1. from Visual Studio with the solution opened go to Build -> Publish Selection.
-
-
-
-2. a new tab will open where you can select the target location, framework and runtime. Here you MUST select linux as Target Runtime.
-
-
-
-3. from there click on "Show all settings", and in the "File publish options" click on the "Produce single file" checkbox.
-
-
-
-4. after that, click on "Save" and then "Publish"
-
-
-
-5. you can open the Target location by clicking on either the "Open folder" link or the path.
-
-6. now you have the solution on a single file.
-
-
diff --git a/PVATestFramework/PVATestFramework.Tests/DataverseTests.cs b/PVATestFramework/PVATestFramework.Tests/DataverseTests.cs
deleted file mode 100644
index 411caf9c..00000000
--- a/PVATestFramework/PVATestFramework.Tests/DataverseTests.cs
+++ /dev/null
@@ -1,150 +0,0 @@
-using PVATestFramework.Console.Helpers.Dataverse;
-using PVATestFramework.Console.Helpers.FileHandler;
-using PVATestFramework.Console.Models.Dataverse;
-using Moq;
-using Moq.Protected;
-using System.Net;
-
-namespace PVATestFramework.Console.Tests
-{
- public class DataverseTests
- {
- private DataverseOptions _dvOptions;
- private Mock _fileHandler;
- private Mock _mockMessageHandler;
- private readonly string _botId = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
- private readonly string _tenantId = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
- private readonly string _clientId = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
- private readonly string _clientSecret = "secret";
- private readonly string _botUri = $"/api/data/v9.2/conversationtranscripts?$top=5&$filter=_bot_conversationtranscriptid_value%20eq%20aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
- private readonly string _environmentUrl = "https://testname.crm.dynamics.com/";
- private readonly string _dvTranscript = Directory.GetCurrentDirectory() + @"\Files\Dataverse\download.json";
-
- [SetUp]
- public void Setup()
- {
- _dvOptions = new DataverseOptions()
- {
- BotId = _botId,
- TenantId = _tenantId,
- ClientId = _clientId,
- ClientSecret = _clientSecret,
- EnvironmentUrl = _environmentUrl
- };
-
- _fileHandler = new Mock();
- _fileHandler.Setup(file => file.CheckFilePath(It.IsAny()));
- _fileHandler.Setup(file => file.WriteToFile(It.IsAny(), It.IsAny()));
-
- _mockMessageHandler = new Mock();
- _mockMessageHandler
- .Protected()
- .Setup>("SendAsync",
- ItExpr.Is(req => req.RequestUri.Host.Contains("login.microsoftonline.com")),
- ItExpr.IsAny())
- .ReturnsAsync(new HttpResponseMessage
- {
- StatusCode = HttpStatusCode.OK,
- Content = new StringContent("{\"access_token\":\"THIS_IS_A_MOCKED_TOKEN\"}")
- });
-
- _mockMessageHandler
- .Protected()
- .Setup>("SendAsync",
- ItExpr.Is(req => req.RequestUri.AbsolutePath.Contains("bots")),
- ItExpr.IsAny())
- .ReturnsAsync(new HttpResponseMessage
- {
- StatusCode = HttpStatusCode.OK,
- Content = new StringContent("{\"@odata.context\":\"https://testname.crm.dynamics.com/api/data/v9.2/$metadata#bots\"}")
- });
-
- _mockMessageHandler
- .Protected()
- .Setup>("SendAsync",
- ItExpr.Is(req => req.RequestUri.AbsolutePath.Contains("conversationtranscripts")),
- ItExpr.IsAny())
- .ReturnsAsync(new HttpResponseMessage
- {
- StatusCode = HttpStatusCode.OK,
- Content = new StringContent(File.ReadAllText(_dvTranscript))
- });
- }
-
- [Test]
- public async Task GetJsonFromDataverseAsyncPassed()
- {
- var httpClient = new HttpClient(_mockMessageHandler.Object);
- var dataverseConnector = new Mock(_dvOptions, httpClient, _fileHandler.Object);
- var result = await dataverseConnector.Object.GetJsonFromDataverseAsync(string.Concat(_environmentUrl, _botUri), false, "transcript.json");
- Assert.IsTrue(result);
- }
-
- [Test]
- public async Task GetJsonFromDataverseAsyncInvalidToken()
- {
- _mockMessageHandler
- .Protected()
- .Setup>("SendAsync",
- ItExpr.Is(req => req.RequestUri.Host.Contains("login.microsoftonline.com")),
- ItExpr.IsAny())
- .ReturnsAsync(new HttpResponseMessage
- {
- StatusCode = HttpStatusCode.OK,
- Content = new StringContent("{\"error_description\":\"token error\"}")
- });
-
- var httpClient = new HttpClient(_mockMessageHandler.Object);
- var dataverseConnector = new Mock(_dvOptions, httpClient, _fileHandler.Object);
- var result = await dataverseConnector.Object.GetJsonFromDataverseAsync(string.Concat(_environmentUrl, _botUri), false, "transcript.json");
- Assert.IsFalse(result);
- }
-
- [Test]
- public async Task GetJsonFromDataverseAsyncInvalidClientId()
- {
- _mockMessageHandler
- .Protected()
- .Setup>("SendAsync",
- ItExpr.Is(req => req.RequestUri.AbsolutePath.Contains("bots")),
- ItExpr.IsAny())
- .ReturnsAsync(new HttpResponseMessage
- {
- StatusCode = HttpStatusCode.Forbidden
- });
-
- var httpClient = new HttpClient(_mockMessageHandler.Object);
- var dataverseConnector = new Mock(_dvOptions, httpClient, _fileHandler.Object);
- var result = await dataverseConnector.Object.GetJsonFromDataverseAsync(string.Concat(_environmentUrl, _botUri), false, "transcript.json");
- Assert.IsFalse(result);
- }
-
- [Test]
- public async Task GetJsonFromDataverseAsyncInvalidBotId()
- {
- _mockMessageHandler
- .Protected()
- .Setup>("SendAsync",
- ItExpr.Is(req => req.RequestUri.AbsolutePath.Contains("bots")),
- ItExpr.IsAny())
- .ReturnsAsync(new HttpResponseMessage
- {
- StatusCode = HttpStatusCode.NotFound
- });
-
- var httpClient = new HttpClient(_mockMessageHandler.Object);
- var dataverseConnector = new Mock(_dvOptions, httpClient, _fileHandler.Object);
- var result = await dataverseConnector.Object.GetJsonFromDataverseAsync(string.Concat(_environmentUrl, _botUri), false, "transcript.json");
- Assert.IsFalse(result);
- }
-
- [Test]
- public async Task GetJsonFromDataverseAsyncInvalidFileExtension()
- {
- var httpClient = new HttpClient(_mockMessageHandler.Object);
- var dataverseConnector = new Mock(_dvOptions, httpClient, _fileHandler.Object);
- var result = await dataverseConnector.Object.GetJsonFromDataverseAsync(string.Concat(_environmentUrl, _botUri), false, "transcript");
- Assert.IsFalse(result);
- }
- }
-}
\ No newline at end of file
diff --git a/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript.chat b/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript.chat
deleted file mode 100644
index fbfc302d..00000000
--- a/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript.chat
+++ /dev/null
@@ -1,8 +0,0 @@
-user: buy items
-bot: I am happy to help you place your order.
-
-user: buy online
-bot: I am happy to help you place your order.
-
-user: buy product
-bot: I am happy to help you place your order.
diff --git a/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript_bot.chat b/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript_bot.chat
deleted file mode 100644
index 9a4d5d73..00000000
--- a/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript_bot.chat
+++ /dev/null
@@ -1,2 +0,0 @@
-user: buy items
-bot:
diff --git a/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript_failed.chat b/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript_failed.chat
deleted file mode 100644
index e5c62591..00000000
--- a/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript_failed.chat
+++ /dev/null
@@ -1,3 +0,0 @@
-user: buy items
-bot: I am happy to help you place your order.
-invalid line
diff --git a/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript_sugestion.chat b/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript_sugestion.chat
deleted file mode 100644
index 247430e9..00000000
--- a/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript_sugestion.chat
+++ /dev/null
@@ -1,4 +0,0 @@
-user: nutrients
-suggested: Open Hours|Buy Vitamins
-bot: To clarify, did you mean:
-user: buy vitamins
diff --git a/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript_sugestion_failed.chat b/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript_sugestion_failed.chat
deleted file mode 100644
index 0506481f..00000000
--- a/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript_sugestion_failed.chat
+++ /dev/null
@@ -1,4 +0,0 @@
-user: nutrients
-suggested:
-bot: To clarify, did you mean:
-user: buy vitamins
diff --git a/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript_user.chat b/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript_user.chat
deleted file mode 100644
index 1169674c..00000000
--- a/PVATestFramework/PVATestFramework.Tests/Files/Chat/basic_transcript_user.chat
+++ /dev/null
@@ -1,2 +0,0 @@
-user:
-bot: I am happy to help you place your order.
diff --git a/PVATestFramework/PVATestFramework.Tests/Files/Dataverse/download.json b/PVATestFramework/PVATestFramework.Tests/Files/Dataverse/download.json
deleted file mode 100644
index c5d3dd05..00000000
--- a/PVATestFramework/PVATestFramework.Tests/Files/Dataverse/download.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "@odata.context": "https://testname.crm.dynamics.com/api/data/v9.2/$metadata#conversationtranscripts",
- "value": [
- {
- "content": "{\"activities\":[{\"valueType\":\"ConversationInfo\",\"id\":null,\"type\":\"trace\",\"timestamp\":1677590794,\"from\":{\"id\":\"\",\"role\":0},\"channelId\":null,\"value\":{\"isDesignMode\":false,\"locale\":null}},{\"id\":\"D4Qiv3Tns5d6a8vZvaXoAR-us|0000000\",\"type\":\"message\",\"timestamp\":1677590794,\"from\":{\"id\":\"06f22724-9df4-942a-db09-f4bbb9004bb0\",\"role\":1},\"channelId\":\"directline\",\"text\":\"hi\",\"attachments\":[]}]}"
- }
- ]
-}
\ No newline at end of file
diff --git a/PVATestFramework/PVATestFramework.Tests/Files/Fail/adaptive_card.json b/PVATestFramework/PVATestFramework.Tests/Files/Fail/adaptive_card.json
deleted file mode 100644
index f06160f2..00000000
--- a/PVATestFramework/PVATestFramework.Tests/Files/Fail/adaptive_card.json
+++ /dev/null
@@ -1,591 +0,0 @@
-{
- "activities":[
- {
- "valueType":"ConversationInfo",
- "id":null,
- "type":"trace",
- "timestamp":1666713307,
- "from":{
- "id":"",
- "role":0
- },
- "channelId":null,
- "value":{
- "isDesignMode":true,
- "locale":"en-US"
- }
- },
- {
- "id":"1xsV5eyJGRyBmn9fTm2IED-us|0000000",
- "type":"message",
- "timestamp":1666713306,
- "from":{
- "id":"06f22724-9df4-942a-db09-f4bbb9004bb0",
- "role":1
- },
- "channelId":"directline",
- "textFormat":"plain",
- "text":"vitamins",
- "attachments":[
-
- ],
- "channeldata":{
- "cci_trace_id":"8bvzk",
- "traceHistory":null,
- "enableDiagnostics":true,
- "clientActivityID":"1666713306394fb4445zi50b"
- }
- },
- {
- "valueType":"IntentRecognition",
- "id":"e95558789107473e890fd98d73ffd07d",
- "type":"trace",
- "timestamp":1666713307,
- "from":{
- "id":"06f22724-9df4-942a-db09-f4bbb9004bb0",
- "role":0
- },
- "channelId":null,
- "attachments":[
-
- ],
- "replyToId":"1xsV5eyJGRyBmn9fTm2IED-us|0000000",
- "value":{
- "intentId":"new_topic_fb1bdac67dfd431ba6936daeefb6d11e",
- "intentScore":{
- "score":1.0,
- "Type":1,
- "Title":"Buy Vitamins"
- },
- "triggerUtterance":"vitamins",
- "normalizedTriggerUtterance":"vitamins"
- }
- },
- {
- "id":null,
- "type":"message",
- "timestamp":1666713307,
- "from":{
- "id":"06f22724-9df4-942a-db09-f4bbb9004bb0",
- "role":0
- },
- "channelId":null,
- "textFormat":"markdown",
- "text":"Would you like your vitamins to arrive at your home or pick them up on a physical store?",
- "attachments":[
-
- ],
- "replyToId":"1xsV5eyJGRyBmn9fTm2IED-us|0000000",
- "channeldata":{
- "DialogTraceDetail":{
- "DialogTraces":[
- {
- "DialogId":"new_topic_fb1bdac67dfd431ba6936daeefb6d11e",
- "NodeId":"d3b1051d-7af6-418b-bdbb-e0af42b4c267",
- "Route":null
- }
- ]
- },
- "DialogErrorDetail":null,
- "ConversationUnderstandingDetail":{
- "NormalizedQuery":null,
- "RawQuery":null,
- "SelectedOption":null,
- "ClarificationDetail":null,
- "RankedIntents":[
-
- ],
- "ExtractedEntities":[
-
- ]
- },
- "VariableDetail":{
- "Variables":{
-
- }
- },
- "CurrentMessageDetail":{
- "TraceId":null,
- "ConversationId":null,
- "CurrentProblemId":"d2217141-3b6d-4c8f-adac-544e75acc50a",
- "CurrentIntentId":"new_topic_fb1bdac67dfd431ba6936daeefb6d11e",
- "ContentVersion":null,
- "SentTime":"2022-10-25T15:55:07.61595+00:00"
- }
- }
- },
- {
- "id":"1xsV5eyJGRyBmn9fTm2IED-us|0000002",
- "type":"message",
- "timestamp":1666713309,
- "from":{
- "id":"06f22724-9df4-942a-db09-f4bbb9004bb0",
- "role":1
- },
- "channelId":"directline",
- "textFormat":"plain",
- "text":"delivery",
- "attachments":[
-
- ],
- "channeldata":{
- "cci_trace_id":"nqhOj",
- "traceHistory":{
- "dialogId":"new_topic_fb1bdac67dfd431ba6936daeefb6d11e",
- "nodeId":"d3b1051d-7af6-418b-bdbb-e0af42b4c267",
- "incomingRouteExpression":""
- },
- "enableDiagnostics":true,
- "clientActivityID":"1666713309471kr7tt3hunk"
- }
- },
- {
- "valueType":"VariableAssignment",
- "id":"aa89084d42db44448486e7d52643631a",
- "type":"trace",
- "timestamp":1666713309,
- "from":{
- "id":"06f22724-9df4-942a-db09-f4bbb9004bb0",
- "role":0
- },
- "channelId":null,
- "attachments":[
-
- ],
- "replyToId":"1xsV5eyJGRyBmn9fTm2IED-us|0000002",
- "value":{
- "name":"response",
- "id":"new_variable_53742b3a1f04485c878ceca03005a462_2a7926d4431e4c82a31a42b8bc44a536",
- "newValue":"2aefb7ff-993e-4112-adf0-cfd992687ed0",
- "type":"global"
- }
- },
- {
- "id":null,
- "type":"message",
- "timestamp":1666713309,
- "from":{
- "id":"06f22724-9df4-942a-db09-f4bbb9004bb0",
- "role":0
- },
- "channelId":null,
- "textFormat":"markdown",
- "text":"There will be an additional shipping charge of $7.50.",
- "attachments":[
-
- ],
- "replyToId":"1xsV5eyJGRyBmn9fTm2IED-us|0000002",
- "channeldata":{
- "DialogTraceDetail":{
- "DialogTraces":[
- {
- "DialogId":"new_topic_fb1bdac67dfd431ba6936daeefb6d11e",
- "NodeId":"d3b1051d-7af6-418b-bdbb-e0af42b4c267",
- "Route":"##DEFAULT"
- },
- {
- "DialogId":"new_topic_fb1bdac67dfd431ba6936daeefb6d11e",
- "NodeId":"292a1b95-7c8d-4271-ba43-b7005050ee0a",
- "Route":null
- }
- ]
- },
- "DialogErrorDetail":null,
- "ConversationUnderstandingDetail":{
- "NormalizedQuery":null,
- "RawQuery":null,
- "SelectedOption":null,
- "ClarificationDetail":null,
- "RankedIntents":[
-
- ],
- "ExtractedEntities":[
-
- ]
- },
- "VariableDetail":{
- "Variables":{
- "new_variable_53742b3a1f04485c878ceca03005a462_2a7926d4431e4c82a31a42b8bc44a536":"delivery"
- }
- },
- "CurrentMessageDetail":{
- "TraceId":null,
- "ConversationId":null,
- "CurrentProblemId":null,
- "CurrentIntentId":null,
- "ContentVersion":null,
- "SentTime":"2022-10-25T15:55:09.928596+00:00"
- }
- }
- },
- {
- "id":null,
- "type":"message",
- "timestamp":1666713309,
- "from":{
- "id":"06f22724-9df4-942a-db09-f4bbb9004bb0",
- "role":0
- },
- "channelId":null,
- "textFormat":"markdown",
- "text":"Is that acceptable?",
- "attachments":[
-
- ],
- "replyToId":"1xsV5eyJGRyBmn9fTm2IED-us|0000002",
- "channeldata":{
- "DialogTraceDetail":{
- "DialogTraces":[
- {
- "DialogId":"new_topic_fb1bdac67dfd431ba6936daeefb6d11e",
- "NodeId":"292a1b95-7c8d-4271-ba43-b7005050ee0a",
- "Route":"##DEFAULT"
- },
- {
- "DialogId":"new_topic_fb1bdac67dfd431ba6936daeefb6d11e",
- "NodeId":"c6dae011-16de-47c8-ac4c-21f72ee6023a",
- "Route":null
- }
- ]
- },
- "DialogErrorDetail":null,
- "ConversationUnderstandingDetail":{
- "NormalizedQuery":null,
- "RawQuery":null,
- "SelectedOption":null,
- "ClarificationDetail":null,
- "RankedIntents":[
-
- ],
- "ExtractedEntities":[
-
- ]
- },
- "VariableDetail":{
- "Variables":{
- "new_variable_53742b3a1f04485c878ceca03005a462_2a7926d4431e4c82a31a42b8bc44a536":"delivery"
- }
- },
- "CurrentMessageDetail":{
- "TraceId":null,
- "ConversationId":null,
- "CurrentProblemId":null,
- "CurrentIntentId":null,
- "ContentVersion":null,
- "SentTime":"2022-10-25T15:55:09.9442203+00:00"
- }
- }
- },
- {
- "id":"1xsV5eyJGRyBmn9fTm2IED-us|0000005",
- "type":"message",
- "timestamp":1666713311,
- "from":{
- "id":"06f22724-9df4-942a-db09-f4bbb9004bb0",
- "role":1
- },
- "channelId":"directline",
- "textFormat":"plain",
- "text":"Yes",
- "attachments":[
-
- ],
- "channeldata":{
- "cci_trace_id":"YAmd2",
- "traceHistory":{
- "dialogId":"new_topic_fb1bdac67dfd431ba6936daeefb6d11e",
- "nodeId":"c6dae011-16de-47c8-ac4c-21f72ee6023a",
- "incomingRouteExpression":"##DEFAULT"
- },
- "enableDiagnostics":true,
- "clientActivityID":"1666713311792qj8twal2oml"
- }
- },
- {
- "valueType":"VariableAssignment",
- "id":"7074e69f4dff4b20a528bc2b608df1e1",
- "type":"trace",
- "timestamp":1666713312,
- "from":{
- "id":"06f22724-9df4-942a-db09-f4bbb9004bb0",
- "role":0
- },
- "channelId":null,
- "attachments":[
-
- ],
- "replyToId":"1xsV5eyJGRyBmn9fTm2IED-us|0000005",
- "value":{
- "name":"accept",
- "id":"fd477864-6f77-47d3-8da6-6a0b99d66386",
- "newValue":"True",
- "type":"local"
- }
- },
- {
- "id":null,
- "type":"message",
- "timestamp":1666713312,
- "from":{
- "id":"06f22724-9df4-942a-db09-f4bbb9004bb0",
- "role":0
- },
- "channelId":null,
- "textFormat":"markdown",
- "text":"Here its the list of the products we have available right now:",
- "attachments":[
-
- ],
- "replyToId":"1xsV5eyJGRyBmn9fTm2IED-us|0000005",
- "channeldata":{
- "DialogTraceDetail":{
- "DialogTraces":[
- {
- "DialogId":"new_topic_fb1bdac67dfd431ba6936daeefb6d11e",
- "NodeId":"c6dae011-16de-47c8-ac4c-21f72ee6023a",
- "Route":"@equals(parameters('fd477864-6f77-47d3-8da6-6a0b99d66386'), true)"
- },
- {
- "DialogId":"new_topic_fb1bdac67dfd431ba6936daeefb6d11e",
- "NodeId":"b00ec891-e3cb-4987-a17b-0c104157c6de",
- "Route":null
- }
- ]
- },
- "DialogErrorDetail":null,
- "ConversationUnderstandingDetail":{
- "NormalizedQuery":null,
- "RawQuery":null,
- "SelectedOption":null,
- "ClarificationDetail":null,
- "RankedIntents":[
-
- ],
- "ExtractedEntities":[
-
- ]
- },
- "VariableDetail":{
- "Variables":{
- "fd477864-6f77-47d3-8da6-6a0b99d66386":"True",
- "new_variable_53742b3a1f04485c878ceca03005a462_2a7926d4431e4c82a31a42b8bc44a536":"delivery"
- }
- },
- "CurrentMessageDetail":{
- "TraceId":null,
- "ConversationId":null,
- "CurrentProblemId":null,
- "CurrentIntentId":null,
- "ContentVersion":null,
- "SentTime":"2022-10-25T15:55:12.4757086+00:00"
- }
- }
- },
- {
- "valueType":"DialogRedirect",
- "id":"c61f8098bdf648bcac69d3067fc0f0f9",
- "type":"trace",
- "timestamp":1666713312,
- "from":{
- "id":"06f22724-9df4-942a-db09-f4bbb9004bb0",
- "role":0
- },
- "channelId":null,
- "attachments":[
-
- ],
- "replyToId":"1xsV5eyJGRyBmn9fTm2IED-us|0000005",
- "value":{
- "targetDialogId":"new_bot_53742b3a1f04485c878ceca03005a462.VitaminsCard.dialog",
- "targetDialogType":"Regular"
- }
- },
- {
- "id":null,
- "type":"message",
- "timestamp":1666713312,
- "from":{
- "id":"06f22724-9df4-942a-db09-f4bbb9004bb0",
- "role":0
- },
- "channelId":"directline",
- "attachments":[
- {
- "contentType":"application/vnd.microsoft.card.adaptive",
- "content":{
- "type":"AdaptiveCard",
- "body":[
- {
- "type":"TextBlock",
- "size":"medium",
- "weight":"bolder",
- "text":"CVS Health Vitamin B",
- "wrap":true
- },
- {
- "type":"TextBlock",
- "size":"medium",
- "weight":"bolder",
- "text":"$11.79",
- "wrap":true
- },
- {
- "type":"TextBlock",
- "text":"Vitamin B supports healthy blood cells and nervous system health.* It is also involved in energy metabolism by converting food into energy.*These statements have not been evaluated by the Food and Drug Administration. This product is not intended to diagnose, treat, cure, or prevent any disease.",
- "wrap":true
- },
- {
- "type":"Container",
- "items":[
- {
- "type":"ActionSet",
- "actions":[
- {
- "type":"Action.Submit",
- "title":"Buy",
- "style":"positive",
- "data":{
- "id":"_qkQW8dJlUeLVi7ZMEzYVw",
- "action":"buy_2"
- }
- }
- ]
- }
- ]
- }
- ],
- "$schema":"http://adaptivecards.io/schemas/adaptive-card.json",
- "version":"1.3"
- }
- },
- {
- "contentType":"application/vnd.microsoft.card.adaptive",
- "content":{
- "type":"AdaptiveCard",
- "body":[
- {
- "type":"ColumnSet",
- "columns":[
- {
- "type":"Column",
- "items":[
- {
- "type":"Image",
- "url":"https://i.imgur.com/0uCIfXd.jpeg"
- }
- ],
- "width":"auto"
- }
- ]
- },
- {
- "type":"TextBlock",
- "size":"medium",
- "weight":"bolder",
- "text":"CVS Health Vitamin C Caplets 1000mg",
- "wrap":true
- },
- {
- "type":"TextBlock",
- "size":"medium",
- "weight":"bolder",
- "text":"$14.99",
- "wrap":true
- },
- {
- "type":"TextBlock",
- "text":"Vitamin C is an essential nutrient that provides antioxidant and immune system health support. Vitamin C is also involved in collagen formation, which is important for healthy skin and connective tissue.\\r\\nDisclaimer: These statements have not been evaluated by the Food and Drug Administration. This product is not intended to diagnose, treat, cure or prevent any disease.",
- "wrap":true
- },
- {
- "type":"Container",
- "items":[
- {
- "type":"ActionSet",
- "actions":[
- {
- "type":"Action.Submit",
- "title":"Buy",
- "style":"positive",
- "data":{
- "id":"_qkQW8dJlUeLVi7ZMEzYVw",
- "action":"buy_3"
- }
- }
- ]
- }
- ]
- }
- ],
- "$schema":"http://adaptivecards.io/schemas/adaptive-card.json",
- "version":"1.3"
- }
- },
- {
- "contentType":"application/vnd.microsoft.card.adaptive",
- "content":{
- "type":"AdaptiveCard",
- "body":[
- {
- "type":"ColumnSet",
- "columns":[
- {
- "type":"Column",
- "items":[
- {
- "type":"Image",
- "url":"https://i.imgur.com/uxPY1SM.jpeg"
- }
- ],
- "width":"auto"
- }
- ]
- },
- {
- "type":"TextBlock",
- "size":"medium",
- "weight":"bolder",
- "text":"Nature Made Vitamin D3 + K2 Softgels, 30 CT",
- "wrap":true
- },
- {
- "type":"TextBlock",
- "size":"medium",
- "weight":"bolder",
- "text":"$16.79",
- "wrap":true
- },
- {
- "type":"TextBlock",
- "text":"Nature Made Vitamin D3 + K2 Softgels are dietary supplements that combine Vitamin D 5000 IU (125 mcg) with Vitamin K2 100 mcg to support strong, healthy bones. Sourced from high-quality ingredients, this gluten free Vitamin Error supplement contains no synthetic dyes and no artificial flavors.",
- "wrap":true
- },
- {
- "type":"Container",
- "items":[
- {
- "type":"ActionSet",
- "actions":[
- {
- "type":"Action.Submit",
- "title":"Buy",
- "style":"positive",
- "data":{
- "id":"_qkQW8dJlUeLVi7ZMEzYVw",
- "action":"buy_4"
- }
- }
- ]
- }
- ]
- }
- ],
- "$schema":"http://adaptivecards.io/schemas/adaptive-card.json",
- "version":"1.3"
- }
- }
- ],
- "replyToId":"1xsV5eyJGRyBmn9fTm2IED-us|0000005"
- }
- ]
-}
\ No newline at end of file
diff --git a/PVATestFramework/PVATestFramework.Tests/Files/Fail/dym.json b/PVATestFramework/PVATestFramework.Tests/Files/Fail/dym.json
deleted file mode 100644
index 0d50522a..00000000
--- a/PVATestFramework/PVATestFramework.Tests/Files/Fail/dym.json
+++ /dev/null
@@ -1,87 +0,0 @@
-{
- "Activities": [
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675179340,
- "From": {
- "Id": "",
- "Role": 1
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "nutrients",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- },
- {
- "ValueType": "IntentCandidates",
- "Id": null,
- "Type": "trace",
- "Timestamp": 1675179340,
- "From": {
- "Id": "",
- "Role": 0
- },
- "ChannelId": null,
- "Value": {
- "triggerUtterance": null,
- "normalizedTriggerUtterance": null,
- "intentCandidates": [
- {
- "intentId": null,
- "intentScore": {
- "score": 0.0,
- "Type": 0,
- "Title": "Open Hours"
- }
- },
- {
- "intentId": null,
- "intentScore": {
- "score": 0.0,
- "Type": 0,
- "Title": "Invalid Suggested Topic"
- }
- },
- {
- "intentId": null,
- "intentScore": {
- "score": 0.0,
- "Type": 0,
- "Title": "Test Vitamins"
- }
- }
- ]
- },
- "TextFormat": null,
- "Text": null,
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- },
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675179340,
- "From": {
- "Id": "",
- "Role": 0
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "To clarify, did you mean:",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- }
- ]
-}
\ No newline at end of file
diff --git a/PVATestFramework/PVATestFramework.Tests/Files/Fail/invalid_json.json b/PVATestFramework/PVATestFramework.Tests/Files/Fail/invalid_json.json
deleted file mode 100644
index 9f559b8a..00000000
--- a/PVATestFramework/PVATestFramework.Tests/Files/Fail/invalid_json.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "Activities": [
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "hi bot",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- }
- ]
-}
\ No newline at end of file
diff --git a/PVATestFramework/PVATestFramework.Tests/Files/Fail/multiple.json b/PVATestFramework/PVATestFramework.Tests/Files/Fail/multiple.json
deleted file mode 100644
index e7ad305c..00000000
--- a/PVATestFramework/PVATestFramework.Tests/Files/Fail/multiple.json
+++ /dev/null
@@ -1,124 +0,0 @@
-{
- "list_of_conversations": [
- {
- "activities": [
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- "Id": null,
- "Role": 1
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "hi bot",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- },
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- "Id": null,
- "Role": 0
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "<(^Hello!$|^Hi there!$|^Good morning!$|^Whats up!$)>",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- }
- ]
- },
- {
- "activities": [
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- "Id": null,
- "Role": 1
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "hi bot",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- },
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- "Id": null,
- "Role": 0
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "Error Activity",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- }
- ]
- },
- {
- "activities": [
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- "Id": null,
- "Role": 1
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "hi bot",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- },
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- "Id": null,
- "Role": 0
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "<(^Hello!$|^Hi there!$|^Good morning!$|^Whats up!$)>",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- }
- ]
- }
- ]
-}
\ No newline at end of file
diff --git a/PVATestFramework/PVATestFramework.Tests/Files/Fail/simple.json b/PVATestFramework/PVATestFramework.Tests/Files/Fail/simple.json
deleted file mode 100644
index a2741959..00000000
--- a/PVATestFramework/PVATestFramework.Tests/Files/Fail/simple.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "Activities": [
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- "Id": null,
- "Role": 1
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "hi bot",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- },
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- "Id": null,
- "Role": 0
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "How can I help you",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- }
- ]
-}
\ No newline at end of file
diff --git a/PVATestFramework/PVATestFramework.Tests/Files/Pass/adaptive_card.json b/PVATestFramework/PVATestFramework.Tests/Files/Pass/adaptive_card.json
deleted file mode 100644
index 199c2a7f..00000000
--- a/PVATestFramework/PVATestFramework.Tests/Files/Pass/adaptive_card.json
+++ /dev/null
@@ -1,49 +0,0 @@
-{
- "list_of_conversations": [
- {
- "activities": [
- {
- "id": "KQSAWjAIYsv8YZI864YcGT-us|0000000",
- "type": "message",
- "timestamp": 1675268142,
- "from": {
- "id": "06f22724-9df4-942a-db09-f4bbb9004bb0",
- "role": 1
- },
- "channelId": "directline",
- "text": "vitamins",
- "attachments": []
- },
- {
- "id": null,
- "type": "message",
- "timestamp": 1675268142,
- "from": {
- "id": "06f22724-9df4-942a-db09-f4bbb9004bb0",
- "role": 0
- },
- "channelId": "directline",
- "attachments": [
- {
- "contentType": "application/vnd.microsoft.card.adaptive",
- "content": {"type":"AdaptiveCard","body":[{"type":"ColumnSet","columns":[{"type":"Column","items":[{"type":"Image","url":"https://i.imgur.com/CNmYsD2.jpeg"}],"width":"auto"}]},{"type":"TextBlock","size":"medium","weight":"bolder","text":"CVS Health Vitamin D Softgels 2000IU","wrap":true},{"type":"TextBlock","size":"medium","weight":"bolder","text":"$9.99","wrap":true},{"type":"TextBlock","text":"Vitamin D is an essential nutrient that works with Calcium to help develop strong bones and teeth.* This high-potency Vitamin D supplement also assists in maintaining a healthy immune system.* These statements have not been evaluated by the Food and Drug Administration. This product is not intended to diagnose, treat, cure, or prevent any disease.","wrap":true},{"type":"Container","items":[{"type":"ActionSet","actions":[{"type":"Action.Submit","title":"Buy","style":"positive","data":{"id":"_qkQW8dJlUeLVi7ZMEzYVw","action":"buy_1"}}]}]}],"$schema":"http://adaptivecards.io/schemas/adaptive-card.json","version":"1.3"}
- },
- {
- "contentType": "application/vnd.microsoft.card.adaptive",
- "content": {"type":"AdaptiveCard","body":[{"type":"ColumnSet","columns":[{"type":"Column","items":[{"type":"Image","url":"https://i.imgur.com/CQjvhfu.jpeg"}],"width":"auto"}]},{"type":"TextBlock","size":"medium","weight":"bolder","text":"CVS Health Vitamin B12 Tablets 1000mcg","wrap":true},{"type":"TextBlock","size":"medium","weight":"bolder","text":"$11.79","wrap":true},{"type":"TextBlock","text":"Vitamin B12 supports healthy blood cells and nervous system health.* It is also involved in energy metabolism by converting food into energy.*These statements have not been evaluated by the Food and Drug Administration. This product is not intended to diagnose, treat, cure, or prevent any disease.","wrap":true},{"type":"Container","items":[{"type":"ActionSet","actions":[{"type":"Action.Submit","title":"Buy","style":"positive","data":{"id":"_qkQW8dJlUeLVi7ZMEzYVw","action":"buy_2"}}]}]}],"$schema":"http://adaptivecards.io/schemas/adaptive-card.json","version":"1.3"}
- },
- {
- "contentType": "application/vnd.microsoft.card.adaptive",
- "content": {"type":"AdaptiveCard","body":[{"type":"ColumnSet","columns":[{"type":"Column","items":[{"type":"Image","url":"https://i.imgur.com/0uCIfXd.jpeg"}],"width":"auto"}]},{"type":"TextBlock","size":"medium","weight":"bolder","text":"CVS Health Vitamin C Caplets 1000mg","wrap":true},{"type":"TextBlock","size":"medium","weight":"bolder","text":"$14.99","wrap":true},{"type":"TextBlock","text":"Vitamin C is an essential nutrient that provides antioxidant and immune system health support. Vitamin C is also involved in collagen formation, which is important for healthy skin and connective tissue.\r\nDisclaimer: These statements have not been evaluated by the Food and Drug Administration. This product is not intended to diagnose, treat, cure or prevent any disease.","wrap":true},{"type":"Container","items":[{"type":"ActionSet","actions":[{"type":"Action.Submit","title":"Buy","style":"positive","data":{"id":"_qkQW8dJlUeLVi7ZMEzYVw","action":"buy_3"}}]}]}],"$schema":"http://adaptivecards.io/schemas/adaptive-card.json","version":"1.3"}
- },
- {
- "contentType": "application/vnd.microsoft.card.adaptive",
- "content": {"type":"AdaptiveCard","body":[{"type":"ColumnSet","columns":[{"type":"Column","items":[{"type":"Image","url":"https://i.imgur.com/uxPY1SM.jpeg"}],"width":"auto"}]},{"type":"TextBlock","size":"medium","weight":"bolder","text":"Nature Made Vitamin D3 + K2 Softgels, 30 CT","wrap":true},{"type":"TextBlock","size":"medium","weight":"bolder","text":"$16.79","wrap":true},{"type":"TextBlock","text":"Nature Made Vitamin D3 + K2 Softgels are dietary supplements that combine Vitamin D 5000 IU (125 mcg) with Vitamin K2 100 mcg to support strong, healthy bones. Sourced from high-quality ingredients, this gluten free Vitamin D3 supplement contains no synthetic dyes and no artificial flavors.","wrap":true},{"type":"Container","items":[{"type":"ActionSet","actions":[{"type":"Action.Submit","title":"Buy","style":"positive","data":{"id":"_qkQW8dJlUeLVi7ZMEzYVw","action":"buy_4"}}]}]}],"$schema":"http://adaptivecards.io/schemas/adaptive-card.json","version":"1.3"}
- }
- ],
- "replyToId": "KQSAWjAIYsv8YZI864YcGT-us|0000005"
- }
- ]
- }
- ]
-}
\ No newline at end of file
diff --git a/PVATestFramework/PVATestFramework.Tests/Files/Pass/dym.json b/PVATestFramework/PVATestFramework.Tests/Files/Pass/dym.json
deleted file mode 100644
index 752104b0..00000000
--- a/PVATestFramework/PVATestFramework.Tests/Files/Pass/dym.json
+++ /dev/null
@@ -1,87 +0,0 @@
-{
- "Activities": [
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675179340,
- "From": {
- "Id": "",
- "Role": 1
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "nutrients",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- },
- {
- "ValueType": "IntentCandidates",
- "Id": null,
- "Type": "trace",
- "Timestamp": 1675179340,
- "From": {
- "Id": "",
- "Role": 0
- },
- "ChannelId": null,
- "Value": {
- "triggerUtterance": null,
- "normalizedTriggerUtterance": null,
- "intentCandidates": [
- {
- "intentId": null,
- "intentScore": {
- "score": 0.0,
- "Type": 0,
- "Title": "Open Hours"
- }
- },
- {
- "intentId": null,
- "intentScore": {
- "score": 0.0,
- "Type": 0,
- "Title": "Buy Vitamins"
- }
- },
- {
- "intentId": null,
- "intentScore": {
- "score": 0.0,
- "Type": 0,
- "Title": "Test Vitamins"
- }
- }
- ]
- },
- "TextFormat": null,
- "Text": null,
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- },
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675179340,
- "From": {
- "Id": "",
- "Role": 0
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "To clarify, did you mean:",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- }
- ]
-}
\ No newline at end of file
diff --git a/PVATestFramework/PVATestFramework.Tests/Files/Pass/multiple.json b/PVATestFramework/PVATestFramework.Tests/Files/Pass/multiple.json
deleted file mode 100644
index 98ce3dfe..00000000
--- a/PVATestFramework/PVATestFramework.Tests/Files/Pass/multiple.json
+++ /dev/null
@@ -1,124 +0,0 @@
-{
- "list_of_conversations": [
- {
- "activities": [
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- "Id": null,
- "Role": 1
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "hi bot",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- },
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- "Id": null,
- "Role": 0
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "<(^Hello!$|^Hi there!$|^Good morning!$|^Whats up!$)>",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- }
- ]
- },
- {
- "activities": [
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- "Id": null,
- "Role": 1
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "hi bot",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- },
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- "Id": null,
- "Role": 0
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "<(^Hello!$|^Hi there!$|^Good morning!$|^Whats up!$)>",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- }
- ]
- },
- {
- "activities": [
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- "Id": null,
- "Role": 1
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "hi bot",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- },
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- "Id": null,
- "Role": 0
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "<(^Hello!$|^Hi there!$|^Good morning!$|^Whats up!$)>",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- }
- ]
- }
- ]
-}
\ No newline at end of file
diff --git a/PVATestFramework/PVATestFramework.Tests/Files/Pass/simple.json b/PVATestFramework/PVATestFramework.Tests/Files/Pass/simple.json
deleted file mode 100644
index 302a1ceb..00000000
--- a/PVATestFramework/PVATestFramework.Tests/Files/Pass/simple.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "Activities": [
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- "Id": null,
- "Role": 1
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "hi bot",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- },
- {
- "ValueType": null,
- "Id": null,
- "Type": "message",
- "Timestamp": 1675095193,
- "From": {
- "Id": null,
- "Role": 0
- },
- "ChannelId": null,
- "Value": null,
- "TextFormat": null,
- "Text": "<(^Hello!$|^Hi there!$|^Good morning!$|^Whats up!$)>",
- "Attachments": null,
- "ReplyToId": null,
- "SuggestedActions": null,
- "LineNumber": 0
- }
- ]
-}
\ No newline at end of file
diff --git a/PVATestFramework/PVATestFramework.Tests/PVATestFramework.Tests.csproj b/PVATestFramework/PVATestFramework.Tests/PVATestFramework.Tests.csproj
deleted file mode 100644
index 5644d1e9..00000000
--- a/PVATestFramework/PVATestFramework.Tests/PVATestFramework.Tests.csproj
+++ /dev/null
@@ -1,81 +0,0 @@
-
-
-
- net8.0
- enable
- enable
-
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- Always
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
-
-
diff --git a/PVATestFramework/PVATestFramework.Tests/RunnerTests.cs b/PVATestFramework/PVATestFramework.Tests/RunnerTests.cs
deleted file mode 100644
index a810737d..00000000
--- a/PVATestFramework/PVATestFramework.Tests/RunnerTests.cs
+++ /dev/null
@@ -1,354 +0,0 @@
-using PVATestFramework.Console.Helpers.DirectLine;
-using PVATestFramework.Console.Helpers.FileHandler;
-using PVATestFramework.Console.Models.DirectLine;
-using Microsoft.Bot.Connector.DirectLine;
-using Moq;
-using Newtonsoft.Json;
-using Serilog;
-
-namespace PVATestFramework.Console.Tests
-{
- public class RunnerTests
- {
- private DirectLineOptions _dlOptions;
- private Mock _directLineClient;
- private Mock _fileHandler;
- private Mock _logger;
- private List _simpleActivityList;
- private List _multipleActivityList;
- private List _simpleAndMultipleActivityList;
- private List _dymActivityList;
- private List _adaptiveCardActivityList;
- private readonly string _passTestFolder = Directory.GetCurrentDirectory() + @"\Files\Pass";
- private readonly string _failTestFolder = Directory.GetCurrentDirectory() + @"\Files\Fail";
- private readonly string _chatFolder = Directory.GetCurrentDirectory() + @"\Files\Chat";
-
- [SetUp]
- public void Setup()
- {
- _dlOptions = new DirectLineOptions("https://tokenEndpoint.test", new Uri("https://regionalEndpoint.test"));
-
- _directLineClient = new Mock();
- _directLineClient.Setup(client => client.SendActivityAsync(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask);
-
- _fileHandler = new Mock();
- _fileHandler.Setup(file => file.CheckFilePath(It.IsAny()));
- _fileHandler.Setup(file => file.DeleteFile(It.IsAny()));
- _fileHandler.Setup(file => file.GetFullPath(It.IsAny())).Returns("x:\\testpath.json");
- _fileHandler.Setup(file => file.WriteToFile(It.IsAny(), It.IsAny()));
- _fileHandler.Setup(file => file.GetFileAttributes(It.IsAny())).Returns(FileAttributes.Normal);
-
- _logger = new Mock();
- _logger.Setup(logger => logger.Information(It.IsAny()));
- _logger.Setup(logger => logger.Warning(It.IsAny()));
- _logger.Setup(logger => logger.Error(It.IsAny()));
- _logger.Setup(logger => logger.Fatal(It.IsAny()));
- _logger.Setup(logger => logger.ForContext(It.IsAny(), It.IsAny