Empowering your Copilot with Custom API for Finance and Operations

Empowering your Copilot with Custom API for Finance and Operations

It's never been easier to deploy and start with D365 applications from an evaluation and testing perspective. Using the UDE (Unified Developer Experience), it's very easy to get a full-blown environment, including Dataverse and Power Platform capabilities, up and running. It's a huge amount of complexity now abstracted to a nearly wizard-like experience. Once deployed, you have both the legal compliance and rigidness of ERP with Finance and Operations and the flexibility and scalability of Power Platform and Dataverse available to you. So, how do we combine these two worlds in a way that makes sense to everyday users or end customers?

One thing I've really started enjoying playing around with lately is the Copilot Studio. Especially now, since we have opened Pandora's box of allowing Copilot to call and execute custom X++ logic, it opens up a ton of scenarios that can be great but also very risky unless you are careful. A Copilot is only as good as you allow it to be, using whatever knowledge you want to share and what actions you want to allow it to perform.

So, I wanted to create a scenario where we leverage the best of many worlds: the complexity and scale of ERP combined with the ease of using a natural language chat agent to simplify what can be a pretty time-consuming task otherwise: order capture.

Create or access your Copilot environment:

I won't go into details on how to spin up your own environment since this is covered in many other articles (see links below). Still, once you have an environment with Finance and Operations and Dataverse/Power Platform, you should also be able to access your Copilot studio and select the proper environment.

Here you will find your default created sidecar Copilot that you can access from within FnO, but for this scenario, I want to be able to publish my own Copilot in other channels such as Teams, so I created my own (ENCopilotCustomerDetails):

Start feeding your Copilot

Once you have your own brand new Copilot, you can start feeding it various components to level up its capabilities, such as giving it access to Azure Devops using a graph connector or connecting Dataverse to give it access to data.

The knowledge sources you add will allow your Copilot to provide contextual answers based on your own data. Still, based on my experience, it requires some training and trial runs to make it work exactly as you want.

Adding actions

Actions are just what they sound like, abilities that your Copilot can execute based on various sources or user input. The list of available actions is similar to what you can use in other parts of Power Platform, but you can, of course, initiate a Power Automate Flow and do pretty much anything you want from there.

For this scenario, we want to add some new capabilities to our Copilot that leverage the Custom API interface from Finance and Operations. So, to get started, we head over to Visual Studio and ensure we are connected to our cloud environment before we start building our objects. I followed the instructions outlined in the Learn article (link below) to get started, so I won't do a step-by-step instruction here. However, it's important to understand how the solution works.

[CustomAPI('Calculate customer balance', 'Calculates the current balance for a customer in the local currency defined for the customer')]
[AIPluginOperationAttribute]
[DataContract]
public final class ENCustomAPICalculateCustomerBalance implements ICustomAPI
{
    private CustAccount accountNum;
    private CurrencyCode currencyCode;
    private boolean customerFound;
    private AmountCur balance;
    CustTable custTable;

    [CustomAPIRequestParameter('The customer account number', true),
        DataMember('accountNumber')]
        public CustAccount parmAccountNum(CustAccount _accountNum = accountNum)
    {
        accountNum = _accountNum;
        return accountNum;
    }

    [CustomAPIResponseProperty('The current customer account balance'),
        DataMember('balance')]
        public AmountCur parmBalance(AmountCur _balance = balance)
    {
        balance = _balance;
        return balance;
    }         

We can expose specific business operations using the CustomAPI framework by building a class that implements the ICustomAPI business logic. We can then implement request and response parameters decorated with attributes explaining what they are for and tying them to the corresponding data types within D365. These attributes will help the Copilot (or the user in Copilot studio) understand what the operation can do.

Of course, we also need to add the logic that will be executed in the run method, in this case, the balanceAllCurrency method from CustTable as defined by the request parameter for the customer account number. If you read the learn article, you will note that the legal entity is hardcoded, but I opted to use the calling user's default company instead to avoid hard-coding. You can, of course, add company as a parameter, but it's all down to your setup and how easily you want to be able to communicate using a chat agent.

 public void run(Args _args)
    {
        custTable = CustTable::find(this.parmAccountNum());
        if (this.parmAccountNum())
        {
            if (custTable)
            {
                this.parmCustomerFound(true);
                this.parmCurrencyCode(custTable.Currency);
                this.parmBalance(custTable.balanceAllCurrency());
            }
        }
        
    }        

To be a little more creative, I added one more custom API that follows a similar approach with request and response parameters, but instead of returning customer balance, it will create a sales order journal for the given customer and item.

 if (custTable)
                {
                    //Create Sales order journal
                    ttsBegin;
                    numberSeq = NumberSeq::newGetNum(SalesParameters::numRefSalesId());
                    numberSeq.used();
 
                    salesTable.SalesId = numberSeq.num();
                    salesTable.initValue();
                    salesTable.CustAccount = custTable.AccountNum;
                    salesTable.initFromCustTable();
                    salesTable.SalesType = SalesType::Journal; 
 
                    if (!salesTable.validateWrite())
                    {
                        throw Exception::Error;
                    }
                    salesTable.insert();
                    
                    salesLine.clear();
                    salesLine.initFromSalesTable(salesTable);
                    salesLine.SalesId = salesTable.SalesId;
                    salesLine.ItemId = this.parmItemId();
                    salesLine.SalesQty = this.parmSalesQty();
                    salesLine.createLine(true, true, true, true, true, true);
                    ttsCommit;

                    //Set response
                    this.parmSalesId(salesTable.SalesId);


                }        

Of course, this could be achieved in a million different ways, and probably a better approach would be to use Power Automate, Commerce Scale Unit, or Dataverse for order capture. However, for this scenario, I wanted to have a surgical custom order creation API that only creates a journal. This could then be processed via workflow or manual conversion to an actual sales order, for example.

Synchronizing your APIs to Dataverse

Once you have your custom API classes and security objects built and deployed, you will find them in your list of custom API:s in FnO

You also need to enable the feature Custom API generation. Note that it is still in preview, so do not use it in Prod unless you know what you are doing.

%EnvironmentUrl%/?mi=CustomApiTable


Once you click "Synchronize," this will bring the API operation over to Dataverse and expose it as an available operation within Copilot studio. If you encounter issues here, make sure to read the instructions properly in the Learn article. In the end, you should see your actions available if you select "Add an action" and import them to your Copilot.

Help your Copilot understand what to do

To allow your Copilot to understand that it should execute the actions, you need to add topics or try enabling generative AI. This means your Copilot will try to figure out the best responses or actions depending on your instructions, knowledge, or actions. Generative AI is still in preview, and for this scenario, I wanted to be more precise, so I used specific Topics to guide my Copilot.

A topic will use specific phrases to capture the user's intent, from which you can then guide your Copilot to take the appropriate action.

Testing your Copilot

So when we tie it all together, we get a Copilot that can check customer balance and then place an order towards the same customer account using custom APIs exposed from D365 Finance and Operations. This can be published to whatever channel you want and executed in the current user context, such as Teams, as a means to support your sales agents:


In summary

To summarize, it is very important to understand each component's functionality and strengths. The custom APIs open up a ton of possibilities. Still, it's also very dangerous since we are exposing core ERP logic in a way that can cause severe problems if not used properly (such as performance, incorrect postings, etc.). You should think twice about what you want to achieve before resorting to X++ development. Is there another way to resolve my requirement or use case?

If used correctly, it gives us more options to further empower our Copilots, especially when we can combine different sources of knowledge and actions that leverage the capabilities of all apps. I will add a new article shortly to further level up this Copilot by allowing it to be checked on hand from the Inventory Visibility app before placing the order.

I have included some reference links below. I suggest you read up on all related Learn articles since they give you good guidance on how to get started. Good luck!

Create AI plugins for copilots with finance and operations business logic (preview) - Finance & Operations | Dynamics 365 | Microsoft Learn

Unified developer experience for finance and operations apps - Power Platform | Microsoft Learn

Article written by Gustav Sundblad - Fast Track Recognized Solution Architect and Senior Solution Architect at Engage Group

要查看或添加评论,请登录

Engage Group的更多文章