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:
- Update Vendor Payment Journal Status
- 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.

Leave a Comment