Building Vendor Payment Approval Integrations and Deep Links in D365 Finance Using OData Actions





Hello! Recently, while working on a D365 Finance integration, I came across a requirement that looked simple on the surface but ended up becoming an interesting implementation of OData Actions and Deep Links.

The requirement was straightforward.

An external approval platform was responsible for reviewing Vendor Payment Journals. Once an approver made a decision, D365 Finance needed to know whether the journal was approved or rejected. Additionally, the approver wanted a direct link back to the exact journal inside D365 Finance instead of navigating through multiple screens and searching manually.

To solve this, I created a custom OData entity called:

VT_VendorPaymentJournalHeaderEntity

and exposed two unbound OData Actions:

  1. Update Vendor Payment Journal Status
  2. Generate Deep Link to Vendor Payment Journal

Let's walk through both.


Action 1 – Update Vendor Payment Journal Status

The first action is called by the external approval platform whenever an approver approves or rejects a payment journal.

[SysODataActionAttribute('vendPaymJourUpdateStatus', false)]

public static str vendPaymJourUpdateStatus(

    DataAreaId      _dataAreaId,

    LedgerJournalId _journalNum,

    boolean         _status)

The action accepts:

  • Legal Entity (DataAreaId)
  • Journal Number
  • Approval Status

Where:

  • true = Approve
  • false = Reject

Before updating anything, I perform a couple of validations.

First, I verify that the journal exists.

Second, I verify that the journal is actually a Vendor Payment Journal.

This validation is important because without it, another journal type could accidentally be passed into the API and updated incorrectly.

if (ledgerJournalTable &&

    ledgerJournalTable.JournalType != LedgerJournalType::Payment)

{

    throw error(

        strFmt(

            'The specified journal %1 is not of type Vendor Payment Journal.',

            _journalNum));

}

Once validation succeeds, the action delegates the processing to a dedicated service class.

This keeps the OData action lightweight and focused purely on request handling while business logic remains reusable and easier to maintain.

The actual update is surprisingly simple:

if (_status)

{

    ledgerJournalTable.markApproved();

}

else

{

    ledgerJournalTable.markRejected();

}

These methods already handle the underlying framework logic correctly.


Action 2 – Generate Deep Link to Vendor Payment Journal

The second action focuses on user experience.

When an approval email is sent, we want the approver to click a link and immediately land on the relevant Vendor Payment Journal.

[SysODataActionAttribute('vendorPaymentJournalGenerateDeepLink', false)]

public static str vendorPaymentJournalGenerateDeepLink(

    DataAreaId      _dataAreaId,

    LedgerJournalId _journalNum)

Before generating the URL, I validate:

  • Legal Entity exists
  • Journal exists
  • Journal is a Vendor Payment Journal

Since journals are company-specific, I use:

changeCompany(_dataAreaId)

to ensure validation occurs in the correct legal entity.

Generating the Deep Link

One framework class that I believe deserves more attention within the D365 community is:

UrlHelper.UrlGenerator

This class provides a supported and secure mechanism for generating deep links.

Instead of manually building URLs, it automatically handles:

  • Company context
  • Query parameter encryption
  • URL construction
  • Navigation to forms

The setup looks like this:

 

IApplicationEnvironment env =

    EnvironmentFactory::GetApplicationEnvironment();

 

System.Uri currentHost =

    new System.Uri(env.Infrastructure.HostUrl);

 

UrlHelper.UrlGenerator generator =

    new UrlHelper.UrlGenerator();

 

generator.HostUrl =

    currentHost.GetLeftPart(System.UriPartial::Authority);

 

generator.Company = strUpr(_dataAreaId);

 

generator.MenuItemName =

    menuItemDisplayStr(LedgerJournalTable5);

 

generator.MenuItemType =

    MenuItemType::Display;

 

generator.Partition =

    getCurrentPartition();

 

generator.EncryptRequestQuery = true;

The menu item points directly to the Vendor Payment Journals form.

Next, we attach the journal number as a request parameter:

queryParams.AddRequestQueryParameter(

    tableStr(LedgerJournalTable),

    fieldStr(LedgerJournalTable, JournalNum),

    _journalNum);

Finally:

System.Uri fullUri =

    generator.GenerateFullUrl();

 

return fullUri.AbsoluteUri;

 

 

The generated URL is fully encrypted and takes the user directly to the target journal

NOTE:

Before using UrlHelper.UrlGenerator and EnvironmentFactory, make sure the following namespaces are included in your class:

using Microsoft.Dynamics.ApplicationPlatform.Environment;
using Microsoft.Dynamics.AX.Framework.Utilities;

These namespaces are required for:

  • EnvironmentFactory::GetApplicationEnvironment()
  • UrlHelper.UrlGenerator

Without them, the deep-link generation code will not compile.


No comments

Building Vendor Payment Approval Integrations and Deep Links in D365 Finance Using OData Actions

Hello! Recently, while working on a D365 Finance integration, I came across a requirement that looked simple on the surface but ended up b...

Powered by Blogger.