Curious what Dynamic People can do for you?

+ 31 (0) 20 303 24 70
Logo
Blogs ERP
09 July 2021 Gonzalo Rios Ley

A new way of thinking for Business Central and Power Automate

think-different-scaled

It is known by everyone that Power Automate can integrate with Business Central seamlessly nowadays. However, if you are a Business Central consultant, you are probably aware of the limitations this solution offers. Contrary to how events work in BC, Power Automate’s triggers/actions do not allow you to integrate your flow’s logic at BC runtime. Let me illustrate:

 

It would be amazing if we could find a way to execute a Power Automate flow exactly at the same time as an event subscriber gets triggered. The reason for this is that there are multiple cases where we would want to change how the ERP behaves, not only the data.

 

Case Study

Let’s think of an imaginary situation where our client doesn’t want Sales Orders to get deleted after posting if “Amount Including VAT” is greater than €500 (it doesn’t make too much sense, but that’s not the point). As BC consultants, we know there is an event that gets triggered in the background that allows us to take this decision by changing the parameter SkipDelete to true:

What we would normally do from a BC perspective is to create a field somewhere in the system, read that field every time the event gets triggered, and evaluate the condition to know if have to skip deletion or not.

But this logic can not be implemented by Power Automate’s BC actions. First, because this event is not included (only Insert, Modify, Delete and some approval flows) and second because BC code execution doesn’t wait on Power Automate’s Flow to be done. It just triggers it and keeps running.

Http Call

Last weekend a colleague of mine, Niels Minnee mentioned my workmate Victor Sijtsma to read this great article (They are great Power Platform consultants, btw).

 

It immediately came to my mind to use the same behavior for Business Central, let’s check the silly implementation of the solution:

Power Automate flow

 

We trigger the flow by an HTTP call requested by Business Central when the event is triggered. We send the whole Sales Header record as the body of the request.

We check if the field “Amount Including VAT” is greater than 500. Depending on the output, we set the variable to true or false, and we send it back on the response body.

Now let’s check the Business Central part:

There are 3 easy procedures. Mainly, we just build a function that subscribes to the corresponding event, takes the record Sales Header, converts it into JSON, and makes the HTTP call to the Power Automate Flow. It finally reads the response and sets the boolean accordingly.

 

codeunit 50100 PowerAutomate
{

//Function 1 the event subscriber

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post", 'OnBeforeDeleteAfterPosting', '', false, false)]
    local procedure OnBeforeDeleteAfterPosting(var SalesHeader: Record "Sales Header"; var SalesInvoiceHeader: Record "Sales Invoice Header"; var SalesCrMemoHeader: Record "Sales Cr.Memo Header"; var SkipDelete: Boolean; CommitIsSuppressed: Boolean; EverythingInvoiced: Boolean);
    var
        SalesHeaderRecRef: RecordRef;
        JsonResponse: JsonObject;
        JToken: JsonToken;
    begin
        SalesHeaderRecRef.Open(Database::"Sales Header");
        SalesHeaderRecRef.GetTable(SalesHeader);
        JsonResponse := CallService(RecToJson(SalesHeaderRecRef));
        if JsonResponse.Get('SkipDelete', JToken) then
            SkipDelete := JToken.AsValue().AsBoolean()
    end;

//Function 2 the HTTP Call

    procedure CallService(RequestBodyJson: JsonObject) ResponseBody: JsonObject
    var
        Client: HttpClient;
        RequestHeaders: HttpHeaders;
        RequestContent: HttpContent;
        ResponseMessage: HttpResponseMessage;
        RequestMessage: HttpRequestMessage;
        ResponseText: Text;
        contentHeaders: HttpHeaders;
        RequestUrl: Text;
        Body: Text;
        JsonResponse: JsonObject;
        JsonToken: JsonToken;
    begin
//Change the URL for the one given to you by your Power Automate Flow
        RequestUrl := 'https://prod-44.westus.logic.azure.com:443/workflows/688fd823d7ef472a80adffbbba597a0a/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=QswAv71snh2aGHxnpalHbUBuugvyt8fqC1yYFAxCCbk';
        RequestHeaders := Client.DefaultRequestHeaders();
        RequestBodyJson.WriteTo(Body);
        RequestContent.WriteFrom(Body);
        RequestContent.GetHeaders(contentHeaders);
        contentHeaders.Clear();
        contentHeaders.Add('Content-Type', 'application/json');
        Client.Post(RequestURL, RequestContent, ResponseMessage);
        if ResponseMessage.IsSuccessStatusCode then begin
            ResponseMessage.Content().ReadAs(ResponseText);
            JsonResponse.ReadFrom(ResponseText);
            exit(JsonResponse);
        end;
    end;


//Function 3 Builds the Record as a JSON
    procedure RecToJson(RecRef: RecordRef) RecJson: JsonObject
    var
        FieldRef: FieldRef;
        Field: Record Field;
        pDecimal: Decimal;
        pText: Text;
        pDate: Date;
        pInteger: Integer;
    begin
        Clear(RecJson);
        Field.SetRange(TableNo, RecRef.Number);
        if Field.FindSet() then
            repeat
                FieldRef := RecRef.Field(Field."No.");
                if Field.Class = Field.Class::FlowField then
                    FieldRef.CalcField();
                //Obviously incomplete
                case Field.Type of
                    Field.Type::Decimal:
                        begin
                            pDecimal := FieldRef.Value;
                            RecJson.Add(Field.FieldName, pDecimal);
                        end;
                    Field.Type::Integer:
                        begin
                            pInteger := FieldRef.Value;
                            RecJson.Add(Field.FieldName, pInteger);
                        end;
                    Field.Type::Text, Field.Type::Code:
                        begin
                            pText := FieldRef.Value;
                            RecJson.Add(Field.FieldName, pText);
                        end;
                    Field.Type::Date:
                        begin
                            pDate := FieldRef.Value;
                            RecJson.Add(Field.FieldName, pDate);
                        end;
                    else begin
                            begin
                                RecJson.Add(Field.FieldName, Format(FieldRef.Value));
                            end;
                        end;
                end;
            until Field.Next() = 0;
    end;


}

 

Conclusion

It’s proven one more time that Microsoft is giving us more and more ways of integrating new solutions to offer our customers a better way to work with their systems. Obviously, this example is just a proof of concept to show how we can move part of the decision-making logic into an external software, where functional users can keep working in a low-code environment.

I hope you enjoyed reading the blog. If you found it interesting, it would be nice if you could share it on LinkedIn.

For any questions or suggestions about how to implement this, please reach out to me 🙂 I am more than happy to help you. You can reach me via: gonazalo.rios.ley@dynamicpeople.com or call +31 20 303 24 70.