共用方式為


設定費用 Agent (預覽版)

這很重要

  • 這是生產就緒型預覽功能。
  • 生產就緒型預覽版應受補充使用條款所拘束。

適用於與 ERP 整合的 Dynamics 365 Project Operations 和製造業用 Dynamics 365 Project Operations

費用 Agent 匯集 Microsoft Dynamics 365 Project Operations、財務和營運應用程式、Microsoft Copilot Studio、Power Automate 和 Dataverse 的功能,以使用 AI 自動化費用處理工作流程。 此功能有助於節省時間並減少人工作業,讓您的系統能處理收據並產生費用行及報表給用戶。 該功能使用 Microsoft Power Platform 連接器透過 Dataverse 虛擬實體與 Outlook、Microsoft Teams、使用者行事曆以及財務和營運應用程式環境進行整合。

費用 Agent 包含多個流程,其中三個流程可做為核心協調器:

  • 處理電子郵件 – 此流程每小時掃描一次已設定的信箱資料夾,並將附件儲存為 Dynamics 365 Finance 中的未附加收據。
  • 擷取收據識別碼 – 此流程會挑選未附加的收據,並觸發 Agent 以擷取收據詳細資訊並建立未附加的費用明細。
  • 處理費用報表 ——此流程將未附連的費用項目進行轉換,根據您在應用程式中設定的 群組報表 配置,為每個法人實體生成費用報表。

此外,Agent 與 Microsoft Teams 整合,並允許使用調適型卡片進行費用報表審查和提交。

Agent 依賴於數個 Microsoft Power Platform 連接器。 提供的 Power Automate 流程中會自動參考這些連接器。

  • Outlook (Office 365) – 此連接器會存取共用信箱以擷取收據。
  • Dataverse (虛擬實體) – 此連接器透過虛擬實體與財務和營運應用程式整合。
  • Microsoft Copilot Studio – 此連接器會叫用 AI 模型來擷取收據資訊。
  • Microsoft Teams – 此連接器會傳送調適型卡片以進行使用者互動 (如果已啟用 Teams 整合)。
  • Microsoft 365 使用者 – 此連接器會擷取使用者行事曆詳細資料 (選用,如果收據剖析是內容感知的)。

先決條件

  1. 財務與營運環境: 安裝代理程式時,至少需要財務與營運環境的版本 10.0.44(10.0.2263.167 及以上)或 10.0.45(10.0.2345.102 及更新版本)。
  2. 設定費用代理使用者所需的角色: 要完成本文步驟,您必須是該組織的系統管理員,並具備以下角色來設定費用代理使用者以安裝費用代理。
System Role 評論
Power Platform 系統管理中心 系統管理員
  1. 移至 Power Platform 系統管理中心
  2. 移至左窗格中的管理。 選取環境,然後選取您的環境。
  3. 存取>使用者區段上,選取查看全部
  4. 選取使用者,然後選取管理角色並新增角色。
Finance and Operations 系統管理員
  1. 開啟環境的財務和營運 URL。
  2. 移至模組>系統管理>使用者,然後選取使用者。
  3. 選取新增角色 – 系統管理員。
Microsoft 365 Exchange 管理員和使用者管理員
  1. 移至 Microsoft 365 系統管理中心
  2. 移至使用者>使用中使用者>並選取使用者。
  3. 選取管理角色,然後從角色選取 Exchange 管理員
  4. 儲存變更。
  5. 依照相同的步驟來新增使用者管理員角色。
Teams 系統管理中心 Teams 系統管理員 如果您打算啟用 Microsoft Teams 整合,則為必要

設定費用 Agent 的步驟

要安裝並設定費用代理,請遵循以下步驟:

  1. 為財務和運營應用安裝 Copilot。
  2. 在您的環境中啟用 Agent 功能。
  3. 建立費用使用者以執行 Agent。
  4. 設定共用信箱。
  5. 設定費用 Agent。
  6. 在 Microsoft Teams 中啟用費用 Agent (選用 - 如果您需要 Microsoft Teams 整合)

以下章節將詳細說明每個步驟。

第1步:為財務和運營應用安裝 Copilot

費用 Agent 可做為適用於財務和營運應用程式的 Copilot 套件的一部分使用。 安裝此套件後,你會自動取得所有必要的資產,包括代理程式、環境變數和 Power Automate 流程。

要安裝所需的應用程式,請依照以下步驟操作:

  1. 在瀏覽器中前往 Power Platform 系統管理中心
  2. 從環境清單中,選取要安裝應用程式所在環境的名稱。
  3. 在環境的詳細頁面(而非左側導覽列),前往 資源 區段,選擇 Dynamics 365 應用程式
  4. 在 Dynamics 365 應用程式清單中搜尋適用於財務和營運應用程式的 Copilot 。 如果已安裝且有可用的更新,請選取更新按鈕。
  5. 如果應用程式未列在 Dynamics 365 應用程式下,請選取安裝應用程式、選取適用於財務和營運應用程式的 Copilot,然後依照提示完成安裝。

備註

啟用財務和營運應用程式中的 Copilot 功能中,深入了解如何在您的環境中啟用 Copilot。

小提示

要確認套件是否成功安裝,請依照以下步驟操作:

  1. 移至 Power Apps Maker Portal > 選取您的環境 > 選取解決方案 > 查看歷程記錄 > 搜尋並選取 msdyn_ExpenseAI > 詳細資料。
  2. 請檢查 結果 欄位。
    1. 如果結果顯示「成功」,則套件已正確安裝。
    2. 如果結果未顯示「成功」,則安裝失敗。
  3. 如果安裝失敗,刪除msdyn_FnOCopilotAnchor(見卸載部分),然後重新安裝財務和營運應用程式的 Copilot。

步驟 2:在環境中啟用 Agent 功能

安裝 Copilot 財務與營運應用程式套件後,請在 Dataverse 及財務與營運環境中啟用費用代理。

在 Dataverse 中啟用功能

在 Power Platform 管理中心開啟 Copilot 功能旗標。 要開啟 Copilot 功能旗標,請依照以下步驟操作:

  1. 移至 Power Platform 系統管理中心
  2. 選取環境>,選取您的環境>設定>產品>,選取功能
  3. 確認已開啟 Copilot 功能旗標。

在財務和營運環境中啟用功能

要在財務與營運應用程式中啟用該代理人,請遵循以下步驟:

  1. 登入您的財務和營運環境
  2. 移至功能管理,並啟用沉浸式首頁功能Agent 管理功能。
  3. 若要設定費用 Agent (設定根據法律實體而定),請移至費用管理>設定>一般>費用管理參數
  4. 費用分錄 Agent 索引標籤上,依照下表所示設定參數。
參數 價值觀 評論
啟用目前法律實體的費用 Agent Yes 切換為以啟用目前法律實體的 Agent。
頻率 每日或每週 設定自動在組織中建立費用報表的頻率。
報表群組依據 差旅或專案 設定為根據專案或差旅將費用組成群組。

步驟 3:建立費用 Agent 使用者以執行 Agent

建立專門的費用 Agent 使用者,確保 Agent 的運作與任何員工的身分無關。 這種方法有助於提高安全性、可管理性和長期可維護性。 雖然你可以使用擁有必要權限的現有使用者帳號,但請使用系統擁有的身份。

在 Microsoft Entra ID 中建立 Expense Agent 使用者

  1. 登入 Azure 入口網站
  2. 從可用的 Azure 服務中,選取 Microsoft Entra ID
  3. Microsoft Entra ID 底下,建立新的使用者。
  4. 選取新增>使用者>建立新使用者,然後輸入下列詳細資料。
    • 使用者主體名稱
    • 選擇正確的網域
    • 顯示名稱
    • 密碼
    • 標示帳戶已啟用
  5. 若要檢視詳細資料並完成使用者建立程序,請選取檢閱 + 建立,然後選取建立
  6. 從使用者頁面 (管理>使用者) 中,選取使用者,然後檢視詳細資料頁面。
  7. 選取編輯屬性、瀏覽至設定索引標籤,然後填寫適當的使用位置。

備註

根據組織原則,您可能需要變更密碼並設定多重要素驗證 (MFA)。 請依照您平常變更密碼和設定 MFA 的步驟操作。

將所需的授權指派給費用 Agent 使用者

要成功安裝 Expense Agent,請將以下授權分配給 Expense Agent 使用者:

  • Dynamics 365 團隊成員授權
  • Microsoft 365 商務基本版或任何涵蓋 Microsoft Teams 和 Outlook 的授權 (例如,包含 Teams 的 Office 365 E5)
  • Power Apps Premium

要分配授權,請依照以下步驟進行:

  1. 使用有權指派授權、具備管理員或更高權限的使用者帳戶登入 Microsoft 365 系統管理中心
  2. 選取帳單>授權>Dynamics 365 團隊成員授權
  3. 選取 +指派授權
  4. 搜尋在上一個步驟中建立的費用 Agent 使用者。
  5. 選取 [指派] 以完成授權指派。
  6. 同樣依照步驟 2 至 5 取得其他授權 – Microsoft 365 商務版基本版和 Power Apps Premium。

備註

利用使用中使用者頁面指派或取消指派授權中深入了解如何檢查和指派授權。

將使用者添加到Power Platform環境

要將使用者加入 Power Platform 環境,請遵循以下步驟:

  1. 登入 Power Platform 系統管理中心,並選取適當的環境。

    小提示

    此頁面提供與 Dataverse 環境識別碼、Dataverse 環境 URL、財務和營運 URL 相關的資訊。 儲存這些值,以便在後續章節中使用。

  2. 移至存取>使用者>查看全部

  3. 選取新增使用者、輸入新建立的 Agent 使用者,然後選取新增

  4. 管理安全角色 頁面,新增以下角色。

    • 費用 AI Agent 角色
    • 財務和營運 Agent 組態管理員
    • 系統自訂員
  5. 若要確認角色指派,請選取儲存

這些角色提供對 Agent 運作所需 Dataverse 和 Power Automate 元件的存取權。

小提示

如果使用者已經存在,且你只需要指派角色,請到 Power Platform 管理中心,選擇適當的環境。

  1. 移至存取>使用者>查看全部
  2. 選取已建立的 Agent 使用者。
  3. 選取管理角色,然後指派角色。

在財務和營運環境中指派所需的角色

要在財務與營運環境中指派 ExpenseAgentRole 角色,請遵循以下步驟:

  1. 在財務和營運環境中,移至系統管理>使用者
  2. 為 Agent 使用者建立使用者記錄。
  3. 建立使用者後,前往使用者角色區塊,選擇 指派角色,並搜尋 ExpenseAgentRole
  4. 選取 [儲存]。

備註

ExpenseAgentRole 可在財務和營運應用程式版本 10.0.44 (10.0.2263.81)10.0.45 (10.0.2345.6) 中使用,並可與適用於財務和營運應用程式的 Copilot 版本 1.0.3121.1 搭配使用

指派共用信箱存取的權限

Agent 使用者必須具有 Mail.Read.Shared Microsoft Graph 權限。 此權限可讓 Agent 在流程執行期間從已設定的共用信箱讀取收據。

要分配共用信箱存取權,請依照以下步驟操作:

  1. 進入 Microsoft Graph Explorer ,並使用 已建立的代理使用者登入。
  2. 選取右上角的使用者>圖示,選取同意權限
  3. 選取郵件>的下拉式功能表、尋找Mail.Read.Shared>、選取同意,然後選取接受

已建立 Agent 使用者所需角色的摘要

環境 角色 評論
Dataverse
  • 費用 AI Agent 角色
  • 財務和營運 Agent 組態管理員
  • 系統自訂員
  • 上述角色可讓 Agent 與連接至 Dynamics 365 Finance 的 Power Automate 流程、環境變數和虛擬實體進行互動
    Finance and Operations
  • ExpenseAgentRole
  • 系統使用者
  • Agent 必須有此角色才能在財務和營運應用程式環境中建立和管理費用分錄。

    注意:ExpenseAgentRole 可在財務和營運應用程式版本 10.0.44 (10.0.2263.81)10.0.45 (10.0.2345.6) 中使用,並可與適用於財務和營運應用程式的 Copilot 版本 1.0.3121.1 搭配使用
    使用 Graph 總管存取共用信箱 Mail.Read.Shared Microsoft Graph 權限,可讓 Agent 在流程執行期間從已設定的共用信箱讀取收據

    步驟 4:設定共用信箱

    費用 Agent 使用共用信箱來接收和處理收據電子郵件。 擁有 Exchange 管理員角色的使用者需在 Microsoft 365 管理中心建立並設定此信箱。

    要建立和設定共用信箱,請依照以下步驟操作:

    1. 使用 Exchange 管理員帳戶登入 Microsoft 365 系統管理中心

    2. 在左窗格中,選取團隊和群組>共用信箱。

      小提示

      你可能需要選擇 「全部顯示」 來展開完整清單。

    3. 選擇 「添加共用郵箱」。。

    4. 輸入共用郵箱的名稱和電子郵件位址。

    5. 選取儲存變更

    6. 後續步驟底下,選取將成員新增至此共用信箱。 (成員管理可能需要幾分鐘才可供使用)。

    7. 選取新增成員

    8. 選取已建立的 Agent 使用者和任何應該監視信箱的其他人,然後選取新增

    9. 選取 關閉

    備註

    下一步你使用共用信箱的電子郵件地址。 在你設定共享信箱後,在設定時間與費用代理時,必須提供其電子郵件地址和資料夾路徑(預設設為收件匣)作為環境變數。 欲了解更多資訊,請參閱 步驟5:設定費用代理人

    步驟 5:設定費用 Agent

    有兩個用於設定費用 Agent 的選項:

    • 選項A:建議使用 PowerShell 腳本
    • 選項 B:在 Power Apps 中執行手動設定 (無 PowerShell)

    這很重要

    在安裝費用代理程式前,請確保代理程式已在 Microsoft Copilot Studio 中成功配置。

    要確認代理程式是否成功配置,請依照以下步驟操作:

    1. 登入 Microsoft Copilot Studio 並選取您的環境。
    2. 移至 Agent,並搜尋 ExpenseAgent-Line (預覽版)
    3. 請確認「 發佈 」按鈕是否啟用。
    4. 如果已啟用,則繼續安裝。 如果已停用,請等待佈建 Agent。
    5. 重複這些步驟以確認費用分錄 Agent (預覽版) 是否已啟用。

    小提示

    如果 Copilot 財務與營運應用程式的配置時間超過 5-6 小時,請卸載並重新安裝該應用程式,以解決可能的設定延遲。 欲了解更多資訊,請參閱本文末尾的 「卸載費用代理 」章節。

    Agent 的手動設定涉及建立和連結連線、啟用 Power Automate 流程,以及發佈解決方案。 此程序可能非常耗時且容易出錯。 要自動化設定,更新所需參數後使用 PowerShell 腳本。

    PowerShell 指令碼可自動化下列工作:

    • 更新所需的環境變數。
    • 將 Microsoft Power Platform 連線與解決方案連線參考連結。
    • 啟用時間和費用 Agent 所需的所有 Power Automate 流程。
    • 發佈 Copilot Agent。
    • 發布 Dataverse 解決方案。

    執行指令碼之前,您必須建立連線,因為您需要為 install.ps1 檔案中的每個連接器提供連線識別碼。 要建立這些連結,請使用已建立的代理使用者依照以下步驟操作。

    1. 使用新建立的代理使用者登入 Power Apps Maker 入口 網站,並選擇您的環境。
    2. 在左窗格中,選取更多,並選取連線
    3. 選擇 新連線 ,並使用以下表格中的連線名稱搜尋(例如 Office 365 Outlook)。
    4. 從清單中選取適當的連接器並建立連線。
    5. 建立連線之後,記下建立連線的使用者。 理想情況下應該是建立的 Agent 使用者識別碼。 在你下一步建立的安裝檔案中更新這個使用者 ID。
    6. 對下表中列出的其餘每個必要連線重複步驟 3 和 4。
    連線名稱 連線 URL 格式
    Office 365 Outlook https://make.powerapps.com/environments/environmentID/connections
    / shared_office365/connectionID/details
    Office 365 使用者 https://make.powerapps.com/environments/environmentID/connections
    / shared_office365users/connectionID/details(詳細資料)
    Microsoft 團隊 https://make.powerapps.com/environments/environmentID/connections
    / shared_teams/connectionID/details(詳細資料)
    Microsoft Dataverse https://make.powerapps.com/environments/environmentID/connections
    / shared_commondataserviceforapps/connectionID/details(詳細資料)
    Microsoft Copilot Studio (預覽版) https://make.powerapps.com/environments/environmentID/connections
    / shared_microsoftcopilotstudio/connectionID/details(詳細資料)

    建立安裝檔案所需的資訊

    要建立安裝檔案 - install.ps1,請提供以下資訊。 (可參閱下表做為參考依據)。

    參數 其他詳細資料
    Dataverse 環境識別碼 輸入從 Power Platform 系統管理中心取得的環境識別碼。
    範例值:xxxx-xxxx-xxxx-xxx-xxxxxxxxxx
    Dataverse 環境 URL 輸入 Power Platform 系統管理中心的環境 URL。
    注意:確保開頭有 https://,且結尾沒有正斜線 “/”。
    範例值:https://org123.crm.contoso.com
    財務與營運執行個體 URL 以下列格式輸入財務和營運環境詳細資源。
    範例值:https://org123.contoso.com
    注意:確保開頭有 https://,且結尾沒有正斜線‘/’
    OutlookFolderPath 輸入共用信箱中建立的資料夾路徑。 如果沒有建立其他資料夾,預設會將其設定為收件匣。
    範例值:收件匣
    作為最佳做法,建立一個專門用於費用管理的資料夾
    信箱地址識別碼 輸入新建立的共用信箱的郵箱地址
    範例值:expenseagent@contoso.com
    Microsoft Dataverse 連線名稱
    Microsoft Copilot Studio 連線名稱
    Microsoft Office Outlook 連線名稱
    Microsoft Office 365 使用者連線名稱
    Microsoft Teams 連線名稱
    所有連線名稱的輸入都相同,均為所建立代理使用者的電子郵件 ID。

    範例值:createdexpenseagentuser@contoso.com

    建立安裝指令檔

    複製以下程式碼建立安裝腳本檔案。 將所需的環境變數插入腳本,然後用 PowerShell 執行腳本。

    備註

    將安裝腳本檔案放在本地桌面。 不要把它存進 One Drive。

    建立一個包含以下程式碼的 PowerShell 腳本檔案。 執行腳本前先更新上述參數。

    小提示

    當 Mandatory = $true 時,PowerShell 會提示你互動式輸入參數,因此不需要直接在腳本檔中更新參數。

    如果你想避免手動輸入,並想在安裝腳本中預先定義參數,請在以下範例程式碼的參數區段設定 Mandatory = $false。

    將以下程式碼複製到安裝腳本檔,並存為「Install.ps1」。 在參數區塊中更新相應參數欄位的變數。 你需要更新 10 個變數

    小提示

    使用上表做為參考,並將所有範例值取代為您各自的詳細資料。

    #requires -Version 7
    Param(
    
       [Parameter(Mandatory=$true, HelpMessage="Dataverse environment id")]
       [string]$DataverseEnvironmentId = "xxxx-xxxx-xxxx-xxx-xxxxxxxxxx", 
    
       [Parameter(Mandatory=$true, HelpMessage="Dataverse environment URL")]
       [string]$DataverseUrl = "https://org123.crm.dynamics.com",
    
       [Parameter(Mandatory=$true, HelpMessage="Finance and Operations instance URL")]
       [string]$D365FinanceAndOperationsUrl = "https://org123.operations.dynamics.com",
    
       [Parameter(Mandatory=$true, HelpMessage="OutlookFolderPath")]
       [string]$OutlookFolderPath = "Inbox",
    
       [Parameter(Mandatory=$true, HelpMessage="Mailbox Address Id")]
       [string]$MailboxAddressId = "expenseagent@contoso.com",
    
       [Parameter(Mandatory=$true, HelpMessage="Microsoft Dataverse connection name")]
       [string]$MicrosoftDataverseConnectionName = "createdexpenseagentuser@contoso.com",
    
       [Parameter(Mandatory=$true, HelpMessage="Microsoft Copilot Studio connection name")]
       [string]$MicrosoftCopilotStudioConnectionName = "createdexpenseagentuser@contoso.com",
       
       [Parameter(Mandatory=$true, HelpMessage="Microsoft Office Outlook connection name")]
       [string]$Office365OutlookConnectionName = "createdexpenseagentuser@contoso.com",
    
       [Parameter(Mandatory=$true, HelpMessage="Microsoft Office 365 Users connection name")]
       [string]$Office365UsersConnectionName = "createdexpenseagentuser@contoso.com",
    
       [Parameter(Mandatory=$true, HelpMessage="Microsoft Teams connection name")]
       [string]$MicrosoftTeamsConnectionName = "createdexpenseagentuser@contoso.com",
    
       [Parameter(Mandatory=$false, HelpMessage="Checks for bot Sync Errors and if there is provisioning required before Agent publish step")]
       [boolean]$CheckBotSyncStatusAndProvisionBots = $false
    
    )
    
    $flows = @(
        "expense entry retry check",
        "expense configuration",
        "get expense outlook folder",
        "generate expense report",
        "send expense report adaptive card",
        "auto match expenses",
        "process emails",
        "extract unattached receipt ids for copilot invocation",
        "extract unattached receipt output using dataverse plugin",
        "generate expense line",
        "generate expense line without project id and status id",
        "identify project ids",
        "user calendar events",
        "process expense report using copilot",
        "invoke expense agent for receipt processing"
    )
    
    
    $agents = @(
        "msdyn_ExpenseEntryAgent",
        "msdyn_ExpenseReportAgent"
    )
    
    
    # Check PS version
    if ($PSVersionTable.PSVersion.Major -lt 7) {
        Write-Error 'This script requires at least PowerShell version 7' -ErrorAction Stop
    }
    
    # Install the required modules if not already installed or if the version is not 1.0.40
    if (-not (Get-Module -ListAvailable -Name Microsoft.PowerApps.PowerShell | Where-Object { $_.Version -ge [Version]"1.0.40" })) {
        Write-Host "Microsoft.PowerApps.PowerShell version 1.0.40 not found. Installing..." -ForegroundColor Yellow
        Install-Module -Name Microsoft.PowerApps.PowerShell -RequiredVersion 1.0.40 -Force -AllowClobber -Scope CurrentUser
    } else {
        Write-Host "Microsoft.PowerApps.PowerShell version 1.0.40 is already installed." -ForegroundColor Green
    }
    
    if (-not (Get-Module -ListAvailable -Name Microsoft.PowerApps.Administration.PowerShell | Where-Object { $_.Version -ge [Version]"2.0.147" })) {
        Install-Module -Name Microsoft.PowerApps.Administration.PowerShell -RequiredVersion 2.0.147 -Force -AllowClobber -Scope CurrentUser
    }
    
    # Install the required modules if not already installed
    if (-not (Get-Module -ListAvailable -Name Az.Accounts | Where-Object { $_.Version -ge [Version]"5.0.1"})) {
        Install-Module -Name Az.Accounts -RequiredVersion 5.0.1 -Force -AllowClobber -Scope CurrentUser
    }
    
    # Import required modulesds
    Import-Module Az.Accounts
    Import-Module Microsoft.PowerApps.PowerShell
    Import-Module Microsoft.PowerApps.Administration.PowerShell
    
    # global variable declaration
    $filter = '$filter'
    
    
    function Get-AccessToken {
        # Retrieve the access token for the Dataverse environment
        $accessToken = (Get-AzAccessToken -ResourceUrl "$DataverseUrl" -AsSecureString).Token
        Write-Host "Access token for $userId retrieved successfully." -ForegroundColor Green
        return $accessToken
    }
    
    function Get-AccessTokenPlainText {
        param(
            [Parameter(Mandatory=$true, HelpMessage="Access token for authentication")]
            [securestring]$accessToken
        )
        # Retrieve the access token for the PVA environment
        $token = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
        [Runtime.InteropServices.Marshal]::SecureStringToBSTR($accessToken))
        return $token
    }
    
    function update-EnvironmentVaribleValue {
            param (
            [string]$accessToken,
            [string]$env_key,
            [string]$env_value   # Access token for authentication
        )
    
        try 
        {
            # Get the environment variable definition
            $envVarDefinition = Invoke-RestMethod -Method Get -Uri "$DataverseUrl/api/data/v9.2/environmentvariabledefinitions?$filter=schemaname eq '$env_key'" -Headers @{
                Authorization = "Bearer $accessToken"
            }
    
            if ($envVarDefinition.value -ne $null) {
                $envVarDefId = $envVarDefinition.value[0].environmentvariabledefinitionid
    
                # Get the environment variable value record
                $filterValue = [System.Web.HttpUtility]::UrlEncode("_environmentvariabledefinitionid_value eq $envVarDefId")
                $envVarValue = Invoke-RestMethod -Method Get -Uri "$DataverseUrl/api/data/v9.2/environmentvariablevalues?$filter=$filterValue" -Headers @{
                    Authorization = "Bearer $accessToken"
                }
    
                if ($envVarValue.value -ne $null) {
                    $envVarValueId = $envVarValue.value[0].environmentvariablevalueid
                    # Update the environment variable value
                    Invoke-RestMethod -Method Patch -Uri "$DataverseUrl/api/data/v9.2/environmentvariablevalues($envVarValueId)" -Headers @{
                        Authorization = "Bearer $accessToken"
                        "Content-Type" = "application/json"
                    } -Body (@{ value = $env_value } | ConvertTo-Json -Depth 1)
                    Write-Host "Environment variable updated with name $env_key and value $env_value" -ForegroundColor Green
                } else {
                    Write-Host "Environment variable value not found for $env_key. Skipping..." -ForegroundColor Red
                }
            } 
            else {
                Write-Host "Environment variable definition not found for $env_key. Skipping..." -ForegroundColor Yellow
            }
      }
      catch {
            Write-Host "Failed to update environment variable $env_key. Error: $($_)" -ForegroundColor Red
            throw $_  # Re-throw the error to stop the script if this step is critical
        }
    
    }
    
    function update_EnvironmentVariablesForExpense {
            param (
            [string]$accessToken   # Access token for authentication
        )
    
        write-host "Updating environment variables..." -ForegroundColor Yellow
    
        try 
        {
            update-EnvironmentVaribleValue -accessToken $accessToken -env_key "msdyn_ExpenseFnoInstanceUrl" -env_value $D365FinanceAndOperationsUrl
            update-EnvironmentVaribleValue -accessToken $accessToken -env_key "msdyn_ExpenseAgentOutlookFolderPath" -env_value $OutlookFolderPath
            update-EnvironmentVaribleValue -accessToken $accessToken -env_key "msdyn_ExpenseAgentMailboxAddressId" -env_value $MailboxAddressId
            
        }
        Catch {
            Write-Host "Failed to update environment variables. Error: $($_)" -ForegroundColor Red -ErrorAction Stop
        }
    }
    
    # Function to publish the solution
    function Publish-Solution {
        param (
            [string]$accessToken
        )
    
        Write-Host "Publishing All" -ForegroundColor Yellow
    
        # Construct the API endpoint for publishing the solution
        $uri = "$DataverseUrl/api/data/v9.2/PublishAllXml"
    
    
        # Make the API call
        try {
            Invoke-RestMethod -Method Post `
                -Uri $uri `
                -Headers @{
                    Authorization = "Bearer $accessToken"
                    "Content-Type" = "application/json"
                }
    
            Write-Host "Publish All - Success!" -ForegroundColor Green
        } catch {
            Write-Host "Failed to publish. Error: $($_.Exception)" -ForegroundColor Red
            
        }
    }
    
    function Get-FlowGuidByName {
        param (
            [string]$accessToken,   # Access token for authentication
            [string]$flowName       # Name of the flow to search for
        )
    
        #Write-Host "Retrieving GUID for flow: $flowName" -ForegroundColor Yellow
    
        # Construct the API endpoint with a filter for the flow name
        $encodedFlowName = [System.Web.HttpUtility]::UrlEncode($flowName)
        $uri = "$DataverseUrl/api/data/v9.2/workflows?$filter=name eq '$encodedFlowName'"
    
        try {
            # Make the API call
            $response = Invoke-RestMethod -Method Get `
                -Uri $uri `
                -Headers @{
                    Authorization = "Bearer $accessToken"
                    "Content-Type" = "application/json"
                }
    
            # Check if the flow was found
            if ($response.value.Count -gt 0) {
                $flow = $response.value[0]
                Write-Host "Flow found: $($flow.name) with GUID: $($flow.workflowid)" -ForegroundColor Green
                return $flow.workflowid
            } else {
                Write-Host "No flow found with the name: $flowName" -ForegroundColor Red
                return $null
            }
        } catch {
            Write-Host "Failed to retrieve flow GUID. Error: $($_.Exception.Message)" -ForegroundColor Red
            return $null
        }
    }
    
    
    # Function to activate a Power Automate flow
    function Activate-Flow {
        param (
            [string]$DataverseUrl,  # Dataverse environment URL
            [string]$accessToken,   # Access token for authentication
            [string]$flowId         # GUID of the flow to activate
        )
    
        # Construct the request body
        $body = @{
            "statecode" = 1  # Activated
            "statuscode" = 2 # Activated
        } | ConvertTo-Json -Depth 1 -Compress
    
        # Construct the API endpoint
        $uri = "$DataverseUrl/api/data/v9.2/workflows($flowId)"
    
        # Make the API call
        try {
            Invoke-RestMethod -Method Patch `
                -Uri $uri `
                -Headers @{
                    Authorization = "Bearer $accessToken"
                    "Content-Type" = "application/json"
                } `
                -Body $body
    
            Write-Host "Flow activated successfully." -ForegroundColor Green
        } catch {
            Write-Host "Failed to activate flow. Error: $($_.Exception.Message)" -ForegroundColor Red
        }
    }
    
    function Get-ConnectionRefIdFromLogicalName  {
        param (
            [string]$accessToken,
            [string]$connectionRefLogicalName
        )
        $uri = "$DataverseUrl/api/data/v9.2/connectionreferences?$filter=connectionreferencelogicalname eq '$connectionRefLogicalName'"
        $response = Invoke-RestMethod -Method Get `
        -Uri $uri `
        -Headers @{
            Authorization = "Bearer $accessToken"
            "Content-Type" = "application/json"
        }
    
        if ($response -ne $null) {
            write-host "Connection reference id found: $($response.value[0].connectionreferenceid) " -ForegroundColor Green
            return $response.value[0].connectionreferenceid
        }
        else {
            Write-Host "No connection reference found for logical name: $connectionRefLogicalName" -ForegroundColor Red
            return $null
        }
    }
    
    function Get-ConnectionId {
        param (
            [string]$userProvidedName,
            [string]$providerName
        )
    
        try {
            $matchedConnectionId = $null
            # Added -ErrorAction Stop to ensure the catch block is triggered on failure
            $connections = Get-PowerAppConnection -EnvironmentName $DataverseEnvironmentId -ConnectorNameFilter $providerName -ErrorAction Stop
            
            foreach ($con in $connections) {
                if (($con.ConnectionName -eq $userProvidedName) -or ($con.DisplayName -eq $userProvidedName)) {
                    $matchedConnectionId = $con.ConnectionName
                    break
                }
            }
    
            if ($null -eq $matchedConnectionId) {
                # Use 'throw' to create a terminating error that the calling function can catch
                throw "Unable to find connection '$userProvidedName' for provider '$providerName'."
            }
    
            return $matchedConnectionId
        }
        catch {
            # Catch any errors from Get-PowerAppConnection or the 'throw' statement above
            Write-Error "Failed to get connection ID for '$userProvidedName'. Error: $_"
            throw # Re-throw the error to stop the script if this step is critical
        }
    }
    
    function Get-ConnectionReferenceId {
        param(
            [string]$connectionReferenceLogicalName,
            [securestring]$accessToken
        )
    
        try {
            $uri = "$DataverseUrl/api/data/v9.2/connectionreferences?$filter=connectionreferencelogicalname eq '$connectionReferenceLogicalName'"
            
            # Added -ErrorAction Stop for clarity, though Invoke-RestMethod often terminates on HTTP errors
            $response = Invoke-RestMethod -Method Get -Uri $uri -Authentication Bearer -Token $accessToken -ContentType 'application/json' -ErrorAction Stop
                
            if ($null -eq $response -or $response.value.Count -eq 0) {
                throw "Connection reference not found for logical name '$connectionReferenceLogicalName'."
            }
    
            $connectionReferenceDisplayName = $response.value[0].connectionreferencedisplayname
            $connectionReferenceId = $response.value[0].connectionreferenceid
    
            Write-Host "updating connection $connectionReferenceDisplayName for logical name $connectionReferenceLogicalName)"
            return $connectionReferenceId
        }
        catch {
            Write-Error "Failed to get connection reference ID for '$connectionReferenceLogicalName'. Error: $_"
            throw # Re-throw to notify the calling function
        }
    }
    
    function Set-ConnectionReferenceConnection {
        param (
            [string]$connectionReferenceLogicalName,
            [string]$userProvidedConnectionName,
            [string]$providerName,
            [securestring]$accessToken
        )
    
        try {
    
            # These functions will now throw terminating errors if they fail
            $connectionReferenceId = Get-ConnectionReferenceId -connectionReferenceLogicalName $connectionReferenceLogicalName -accessToken $accessToken
            $connectionId = Get-ConnectionId -userProvidedName $userProvidedConnectionName -providerName $providerName
    
            $body = @{
                "connectionid" = "$connectionId"
            } | ConvertTo-Json -Depth 1
    
            $uri = "$DataverseUrl/api/data/v9.2/connectionreferences($connectionReferenceId)"
            # Write-Host "Updating connection reference URI: $uri with connection id $connectionId"
    
            Invoke-RestMethod -Method Patch -Uri $uri -Authentication Bearer -Token $accessToken -ContentType 'application/json' -Body $body -ErrorAction Stop
        
            Write-Host "Connection reference updated successfully." -ForegroundColor Green
        }
        catch {
            # This block will catch errors from any of the functions called within the try block
            Write-Error "Failed to set connection reference for '$connectionReferenceLogicalName'. Error: $_"
            throw
        }
    }
    
    function Activate-Flows {
        param (
            [string]$accessToken,
            [array]$expenseAIFlows
        )
    
        foreach ($flowName in $expenseAIFlows) {
             Write-Host "Activating flow: $flowName" -ForegroundColor Yellow
    
            # Call the Get-FlowGuidByName function to get the flow GUID
            $flowGuid = Get-FlowGuidByName -dataverseUrl $DataverseUrl -accessToken $accessToken -flowName $flowName
    
            if ($flowGuid -ne $null) {
                # Write-Host "Flow Name: $flowName, Flow GUID: $flowGuid" -ForegroundColor Green
                Activate-Flow -dataverseUrl $DataverseUrl -accessToken $accessToken -flowId $flowGuid
                # Write-Host "Flow Name: $flowName, Flow GUID: $flowGuid Activated" -ForegroundColor Green
            } else {
                Write-Host "Flow Name: $flowName not found." -ForegroundColor Red
            }
        }
    }
    
    
    # Function to retrieve the Agent ID by name
    function Get-AgentIdBySchemaName {
        param (
            [string]$DataverseUrl,
            [string]$accessToken,
            [string]$agentSchemaName
        )
    
        Write-Host "Retrieving agent ID for agent schema: $agentSchemaName" -ForegroundColor Yellow
    
        # Construct the API endpoint to retrieve the bot
        $uri = "$DataverseUrl/api/data/v9.2/bots?$filter=schemaname eq '$agentSchemaName'"
    
        try {
            # Make the API call
            $response = Invoke-RestMethod -Method Get -Uri $uri -Headers @{
                Authorization = "Bearer $accessToken"
                "Content-Type" = "application/json"
            }
    
            if ($response.value.Count -gt 0) {
                $agentId = $response.value[0].botid
                return $agentId
            } else {
                Write-Host "No agent found with the name: $agentSchemaName" -ForegroundColor Red
                return $null
            }
        } catch {
            Write-Host "Failed to retrieve agent ID. Error: $($_)" -ForegroundColor Red
            return $null
        }
    }
    
    function Check-BotSyncErrors {
            param (
            [string]$DataverseUrl,
            [string]$accessToken,
            [string]$botId
        )
    
        Write-Host "Retrieving Sync Status for bot ID: $botId" -ForegroundColor Yellow
    
        # Construct the API endpoint to retrieve the bot
        $uri = "$DataverseUrl/api/data/v9.2/bots($botId)"
        try {
            # Make the API call
            $response = Invoke-RestMethod -Method Get -Uri $uri -Headers @{
                Authorization = "Bearer $accessToken"
                "Content-Type" = "application/json"
            }
    
            if ($null -ne $response.synchronizationstatus) {
                # Parse the JSON string in synchronizationstatus
                $syncStatusObj = $response.synchronizationstatus | ConvertFrom-Json
                $state = $syncStatusObj.currentSynchronizationState.state
                $provisioningStatus = $syncStatusObj.currentSynchronizationState.provisioningStatus
    
                Write-Host "Synchronization State: $state" -ForegroundColor Green
                Write-Host "Provisioning Status: $provisioningStatus" -ForegroundColor Green
    
                if ( $state -contains "Error" -or $provisioningStatus -contains "Error") {
                    Write-Host "Bot has synchronization errors." -ForegroundColor Red
                    return 0
                } else {
                    if ( $state -eq "Synchronized" -or $state -eq 'Synchronizing' -and ($provisioningStatus -eq  "Provisioned" -or $provisioningStatus -eq  "ProvisionedWithoutRegistration")) {
                        Write-Host "Bot synchronization is done." -ForegroundColor Yellow
                        return 1
                    } else {
                        Write-Host "Bot synchronization is in progress." -ForegroundColor Green
                        return 2
                    }
                }
            } else {
                Write-Host "No synchronization status found for bot ID: $botId" -ForegroundColor Red
                return $null
            }
        } catch {
            Write-Host "Failed to retrieve agent ID. Error: $($_)" -ForegroundColor Red
            return $null
        }
    }
    
    
    # Function to provision a PVA bot
    function Provision-Agent {
        param (
            [string]$DataverseUrl,
            [string]$accessToken,
            [string]$agentId
        )
    
        # Construct the API endpoint for publishing the bot
        $uri = "$DataverseUrl/api/data/v9.2/bots($agentId)/Microsoft.Dynamics.CRM.PvaProvision"
    
        try {
            # Make the API call
            Invoke-RestMethod -Method Post -Uri $uri -Headers @{
                Authorization = "Bearer $accessToken"
                "Content-Type" = "application/json"
            }
    
            Write-Host "Agent Provisioning successfully!" -ForegroundColor Green
            # Add 30 second delay to allow the publish process to complete
            Start-Sleep -Seconds 30
            return $true
        } catch {
            Write-Host "Failed to Provision Agent. Error: $($_.Exception.Message)" -ForegroundColor Red
        }
        return $false
    }
    
    
    # Function to publish a PVA bot
    function Publish-Agent {
        param (
            [string]$DataverseUrl,
            [string]$accessToken,
            [string]$agentId
        )
    
        Write-Host "Publishing agent with ID: $agentId" -ForegroundColor Yellow
    
        # Construct the API endpoint for publishing the bot
        $uri = "$DataverseUrl/api/data/v9.2/bots($agentId)/Microsoft.Dynamics.CRM.PvaPublish"
    
        try {
            # Make the API call
            Invoke-RestMethod -Method Post -Uri $uri -Headers @{
                Authorization = "Bearer $accessToken"
                "Content-Type" = "application/json"
            }
    
            Write-Host "Agent published successfully!" -ForegroundColor Green
            # Add 30 second delay to allow the publish process to complete
            Start-Sleep -Seconds 30
        } catch {
            Write-Host "Failed to publish Agent. Error: $($_.Exception.Message)" -ForegroundColor Red
        }
    }
    
    function Publish-Agents {
        param (
            [string]$accessToken,
            [array]$agentSchemas
        )
    
        if (-not $agentSchemas -or $agentSchemas.Count -eq 0) {
            Write-Host "No agent schemas provided. Skipping agent publishing." -ForegroundColor Yellow
            return
        }
    
        foreach ($agentSchema in $agentSchemas) {
            #Write-Host "Publishing agent schema: $agentSchema" -ForegroundColor Yellow
    
            try {
                    # Construct the API endpoint for publishing the agent schema
                    $agentId = Get-AgentIdBySchemaName -dataverseUrl $DataverseUrl -accessToken $accessToken -agentSchemaName $agentSchema
    
                    if ($agentId -ne $null) {
                        # check for sync errors
                        if ($CheckBotSyncStatusAndProvisionBots) {
                            $syncStatus = Check-BotSyncErrors -dataverseUrl $DataverseUrl -accessToken $accessToken -botId $agentId
                            if (0 -eq $syncStatus) {
                                Write-Host "Agent has sync errors. Skipping the publish process. Please check the bot: $agentId details" -ForegroundColor Red
                                continue
                            } elseif (2 -eq $syncStatus) {
                                Write-Host "Agent synchronization is still in progress. reprovisioning the agent." -ForegroundColor Yellow
                                if (Provision-Agent -dataverseUrl $DataverseUrl -accessToken $accessToken -agentId $agentId -eq $false) {
                                    Write-Host "Agent reprovisioning failed. Skipping the publish process. Please check the bot: $agentId details" -ForegroundColor Red
                                    continue
                                }
                            } else {
                                Write-Host "Agent synchronization is done. Proceeding to publish." -ForegroundColor Green
                            }
                        }
                        # Step 4: Publish the bot
                        Publish-Agent -dataverseUrl $DataverseUrl -accessToken $accessToken -agentId $agentId
                    } else {
                        Write-Host "Agent not found. Cannot proceed with publishing.Skipping the step" -ForegroundColor Yellow
                    }
            }
            catch {
                Write-Host "An error occurred while publishing agent schema: $agentSchema. Error: $_" -ForegroundColor Red
            }
        }
    
    }
    
    
    # Main script execution
    try {
    
        $expenseAIFlows = $flows
        $agentSchemas = $agents
    
        # Step 1: Interactive login to Azure
        Connect-AzAccount -UseDeviceAuthentication
        $accessToken = Get-AccessToken
        $accessTokenPlainText = Get-AccessTokenPlainText -accessToken $accessToken
    
        # Step 2: Setup ennviornment variables
        update_EnvironmentVariablesForExpense -accessToken $accessTokenPlainText 
        Write-Host "Environment variables updated successfully!" -ForegroundColor Green
    
        # Step 3: Check active connections
        Set-ConnectionReferenceConnection -userProvidedConnectionName $MicrosoftDataverseConnectionName -providerName "/providers/Microsoft.PowerApps/apis/shared_commondataserviceforapps" -connectionReferenceLogicalName "msdyn_sharedcommondataserviceforapps_2c2d4" -accessToken $accessToken
    
        Set-ConnectionReferenceConnection -userProvidedConnectionName $MicrosoftCopilotStudioConnectionName -providerName "/providers/Microsoft.PowerApps/apis/shared_microsoftcopilotstudio" -connectionReferenceLogicalName "msdyn_sharedmicrosoftcopilotstudio_26d9d" -accessToken $accessToken
    
        Set-ConnectionReferenceConnection -userProvidedConnectionName $Office365OutlookConnectionName -providerName "/providers/Microsoft.PowerApps/apis/shared_office365" -connectionReferenceLogicalName "msdyn_sharedoffice365_9b471" -accessToken $accessToken
    
        Set-ConnectionReferenceConnection -userProvidedConnectionName $MicrosoftTeamsConnectionName -providerName "/providers/Microsoft.PowerApps/apis/shared_teams" -connectionReferenceLogicalName "msdyn_sharedteams_8ea9c" -accessToken $accessToken
    
        Set-ConnectionReferenceConnection -userProvidedConnectionName $Office365UsersConnectionName -providerName "/providers/Microsoft.PowerApps/apis/shared_office365users" -connectionReferenceLogicalName "msdyn_sharedoffice365users_909b9" -accessToken $accessToken
        
    
        # Step 4: Activate flows
        Activate-Flows -accessToken $accessTokenPlainText -expenseAIFlows $expenseAIFlows
    
        # step 5: publish the agents
        Publish-Agents -accessToken $accessTokenPlainText -agentSchemas $agentSchemas
    
        # Step 6: Publish the solution 
        Publish-Solution -accessToken $accessTokenPlainText
    
        Write-Host "Agent setup completed successfully!" -ForegroundColor Green
    
    } catch {
        Write-Host "An error occurred: $_" -ForegroundColor Red
    }
    
    

    要觸發 PowerShell 檔案,請依照以下步驟操作:

    1. 開啟 PowerShell(最低版本需求 - PowerShell 7)。
    2. 回到你儲存檔案的位置。 (使用命令 cd<檔案位置>)
    3. 觸發安裝指令碼。 (使用命令 '.\Install.ps1')
    4. 請依照指示登入 Azure。
    5. 登入後,您可能需要再授權一次。 (使用建立的 Agent 使用者識別碼。)

    等腳本完全執行後,看看「 agent set 成功完成」的訊息!

    備註

    上述指令碼會執行下列動作:

    • 設定環境變數。
    • 驗證並連結連接參考。
    • 啟用 Power Automate 流程。
    • 發佈所需的Copilot代理程式。
    • 發布 Dataverse 解決方案。

    指令碼成功執行後,費用 Agent 已完成所有設定,隨時可供使用。

    選項B:在 Power Apps 手動設定(不要使用 PowerShell)

    如果你不想用 PowerShell 腳本,可以透過 Power Apps 手動設定 Expense Agent。 此過程涉及更新環境變數、啟用Power Automate流和發佈解決方案。

    更新環境變數

    要更新環境變數,請依照以下步驟操作:

    1. 登入 Power Apps,然後選取您的環境。

    2. 選取解決方案,然後開啟預設解決方案 (或安裝 Agent 的解決方案)。

    3. 移至環境變數,然後設定下列值。

      變數名稱 說明
      費用 Agent Outlook 資料夾路徑 指定要在共用信箱中監視的資料夾路徑 (預設為收件匣)。
      費用 Agent 共用信箱地址識別碼 指定共用信箱的電子郵件地址。 若要使用已登入使用者的信箱,請輸入 NA
      財務和營運執行個體 URL 指定財務和營運應用程式環境的 URL (例如 https://org123.contoso.com)。

    啟用 Power Automate 流程

    費用 Agent 依賴於下列 Power Automate 流程:

    • 費用輸入重試檢查
    • 費用設定
    • 取得費用展望資料夾
    • 產生費用報表
    • 傳送費用報表調適型卡片
    • 自動匹配費用
    • 處理電子郵件
    • 提取未附加的收據 ID 以供副手叫用
    • 使用 Dataverse 外掛程式擷取未附加的收據輸出
    • 生成費用明細
    • 產生沒有專案識別碼和狀態識別碼的費用明細
    • 識別專案識別碼
    • 使用者行事曆事件
    • 使用 Copilot 處理費用報表
    • 叫用費用 Agent 以進行收據處理

    要啟用這些流程,請遵循以下步驟:

    1. 登入 Power Automate,然後選取您的環境。

    2. 選取我的流程

    3. 對於前述列表中的15個流程,請依照以下步驟操作:

      1. 找到流程。
      2. 請選取 ,再編輯
      3. 關閉新設計工具選項,以切換至舊設計工具檢視。
      4. 驗證任何必要的連線 (直到出現綠色核取記號)。
      5. 選取繼續,然後選取儲存
      6. 選擇 Turn On (開啟) 以啟用流。

    發佈解決方案

    完成設定所有環境變數和流程之後,依照下列步驟來發佈解決方案。

    1. 在 Power Apps 中,移至解決方案
    2. 選擇您的環境和解決方案。
    3. 選取發佈所有自訂

    完成這些步驟後,費用代理人即已完全設定並可使用。

    步驟 6:在 Microsoft Teams 中啟用費用 Agent (選用)

    若要為費用 Agent 啟用 Teams 型通訊,請將 Teams 管道新增至 Power Apps 中的 Agent。 代理人接著可以透過 Teams 傳送自適應卡片。

    啟用 Teams 管道

    要啟用 Teams 頻道,請遵循以下步驟:

    1. 登入 Copilot Studio,並選取正確的環境。
    2. Agent 索引標籤上,選取費用分錄 Agent
    3. 在 Agent 檢視表的管道索引標籤上,選取 Teams 和 Microsoft 365 Copilot
    4. 選取新增管道以啟用 Teams 整合,然後依照設定 Teams 應用程式可用性區段中的步驟來設定您要與之共用應用程式的人員。

    開啟 Teams + Microsoft 365 管道的設定面板中了解詳細資訊。

    設定 Teams 應用程式的可用性

    要設定 Teams 應用程式的可用性,請依照以下步驟操作:

    1. 創建 Teams 應用後,選擇 可用性選項

    2. 選取您要與之共用應用程式的人員:

      • 組織內的特定使用者
      • 整個組織
    3. 提交應用程式以供審批。

    在 Teams 系統管理中心發佈應用程式

    要在 Teams 管理中心發布應用程式,請依照以下步驟操作:

    1. 登入 Teams 系統管理中心
    2. 前往 Teams 應用程式 > 管理應用程式。 搜尋「費用」並選擇「 費用輸入代理 應用程式」,該處的應用程式狀態被封鎖。
    3. 選取發佈以將應用程式解除封鎖。 發佈動作成功完成後,請確保應用程式狀態變更為未被封鎖。

    連接和設定 Teams 和 Microsoft 365 的 Agent中了解詳細資訊。

    完成這些步驟後,您的支出助理已準備就緒。

    備註

    您也可以透過 Dynamics 365 Finance 環境中的按讚和按倒讚圖示及意見回饋快顯視窗,對代理人所生成的費用項目及報告提供回饋。

    解除安裝費用 Agent

    卸載 費用代理,請遵循以下步驟:

    1. 登入 Microsoft Power Apps Maker 入口網站。
    2. 選取解決方案、搜尋 msdyn_ExpenseAI、選取三個點,然後選取刪除
    3. 搜尋 msdyn_FnOCopilotAnchor,並刪除解決方案。