Friday, 21 August 2026

Convert Quantity Between Units Using Unit Symbols in D365FO

 In Dynamics 365 Finance & Operations (D365FO), unit conversion is a common requirement when working with sales, purchasing, inventory, and warehouse processes.

Instead of hardcoding conversion factors, we can use the standard UnitOfMeasureConversion table to retrieve the product-specific conversion factor.

The following reusable X++ method converts a quantity from one unit to another using the configured conversion setup.

X++ Code

private Qty convertQtyByUnitSymbols(
    ItemId              _itemId,
    UnitOfMeasureSymbol _fromSymbol,
    UnitOfMeasureSymbol _toSymbol,
    Qty                 _qty)
{
    InventTable            inventTable = InventTable::find(_itemId);
    EcoResProduct          product     = inventTable.Product();
    UnitOfMeasure          fromUnit    = UnitOfMeasure::findBySymbol(_fromSymbol);
    UnitOfMeasure          toUnit      = UnitOfMeasure::findBySymbol(_toSymbol);
    UnitOfMeasureConversion conversion;
    Qty                     result      = _qty;

    if (!product.RecId || !fromUnit.RecId || !toUnit.RecId)
    {
        return result;
    }

    // Ensure the item's inventory unit matches the "from" unit
    if (inventTable.inventUnitId() != fromUnit.Symbol)
    {
        return result;
    }

    select firstonly Factor from conversion
        where conversion.Product           == product.RecId
           && conversion.FromUnitOfMeasure == fromUnit.RecId
           && conversion.ToUnitOfMeasure   == toUnit.RecId;

    if (conversion.RecId && conversion.Factor != 0)
    {
        result = _qty * conversion.Factor;
    }

    return result;
}

This approach keeps the conversion simple, reusable, and configuration-driven, using the standard D365FO unit conversion setup rather than hardcoded conversion factors.

Thursday, 20 August 2026

Check Stock Availability for Reservation in D365FO

In D365FO, before reserving inventory, you may need to verify whether the required quantity is actually available for reservation.

The following X++ method checks stock availability based on the Item, Warehouse, WMS Location, and Batch dimensions. It uses the standard InventOnhand framework to retrieve the available physical and ordered quantities.

The method then adds these quantities together and compares the result with the requested quantity. It returns true when sufficient stock is available; otherwise, it returns `false.

public static boolean isStockAvailableToReserve(
    ItemId          _itemId,
    InventLocationId _inventLocationId,
    WMSLocationId   _wmsLocationId,
    InventBatchId   _inventBatchId,
    Qty             _qty)
{
    InventDim       inventDim;
    InventDimParm   inventDimParm;
    InventOnhand    inventOnHand;

    inventDim.InventLocationId = _inventLocationId;
    inventDim.wMSLocationId    = _wmsLocationId;
    inventDim.InventBatchId    = _inventBatchId;

    inventDimParm.InventLocationIdFlag = true;
    inventDimParm.wMSLocationIdFlag    = true;
    inventDimParm.InventBatchIdFlag    = true;

    inventOnHand = InventOnhand::newParameters(
        _itemId,
        inventDim,
        inventDimParm);

    return (inventOnHand.availPhysical() +
            inventOnHand.availOrdered()) >= _qty;
}

How It Works

  • InventDim defines the inventory dimensions to check.
  • InventDimParm specifies which dimensions should be used as filters.
  • InventOnhand retrieves the inventory availability for the specified item and dimensions.
  • availPhysical() returns the currently available physical inventory.
  • availOrdered() returns the available ordered/expected quantity.
  • The method compares the combined available quantity with the requested quantity and returns a Boolean result.

This provides a simple reusable method that can be called before performing an inventory reservation in D365FO.

Wednesday, 19 August 2026

Company Lookup for an Unbounded Control in D365FO

 In D365FO, you can create a Company (DataArea) lookup for an unbounded control using SysTableLookup without requiring any datasource binding.

[FormControlEventHandler(formControlStr(PurchCreateFromSalesOrder, DSS_IntercompanyId), 
FormControlEventType::Lookup)] public static void DSS_IntercompanyId_OnLookup(FormControl sender, FormControlEventArgs e) { SysTableLookup sysTableLookup; Query query; QueryBuildDataSource qbds; sysTableLookup = SysTableLookup::newParameters(tableNum(CompanyInfo), sender); sysTableLookup.addLookupfield(fieldNum(CompanyInfo, DataArea)); sysTableLookup.addLookupfield(fieldNum(CompanyInfo, Name)); query = new Query(); qbds = query.addDataSource(tableNum(CompanyInfo)); // Optional filter // qbds.addRange(fieldNum(CompanyInfo, DataArea)) // .value(SysQuery::valueNotEquals(curExt())); sysTableLookup.parmQuery(query); sysTableLookup.performFormLookup(); }

Key Point

Use CompanyInfo to display available companies/legal entities and pass sender to SysTableLookup::newParameters() for the unbounded control.

ItemId Lookup for an Unbounded Control in D365FO

 In D365FO, you can create an ItemId lookup for an unbounded control using SysTableLookup without requiring any datasource binding.

The key is to pass the control (sender) directly to SysTableLookup::newParameters() and define the required lookup fields and query.

[FormControlEventHandler(formControlStr(PurchCreateFromSalesOrder, MarkupItemId), 
FormControlEventType::Lookup)] public static void MarkupItemId_OnLookup(FormControl sender, FormControlEventArgs e) { SysTableLookup sysTableLookup; Query query; QueryBuildDataSource qbds; sysTableLookup = SysTableLookup::newParameters(tableNum(InventTable), sender); sysTableLookup.addLookupfield(fieldNum(InventTable, ItemId)); sysTableLookup.addLookupfield(fieldNum(InventTable, NameAlias)); query = new Query(); qbds = query.addDataSource(tableNum(InventTable)); // Optional filter // qbds.addRange(fieldNum(InventTable, ItemType)) // .value(queryValue(ItemType::Item)); sysTableLookup.parmQuery(query); sysTableLookup.performFormLookup(); }

Key Point

For an unbounded control, pass sender to SysTableLookup::newParameters(). No datasource binding is required.

How to Confirm a Purchase Order in D365 Finance & Operations Using X++

In Microsoft Dynamics 365 Finance & Operations, a Purchase Order can be confirmed programmatically using the standard PurchFormLetter framework.

This is useful when you need to confirm a Purchase Order from custom business logic, batch processes, integrations, or custom forms without manually clicking Confirm from the Purchase Order form.

The following method accepts a PurchTable record and confirms the Purchase Order using the standard D365 F&O framework.


public static PurchTable confirmPurchaseOrder(PurchTable _purchTable)

{

    PurchFormLetter          purchFormLetter;

    PurchFormletterParmData  purchFormLetterParmData;

    PurchParmUpdate          purchParmUpdate;

    PurchParmTable           purchParmTable;


    ttsBegin;


    // Create parameter data for Purchase Order confirmation

    purchFormLetterParmData = PurchFormletterParmData::newData(

        DocumentStatus::PurchaseOrder,

        VersioningUpdateType::Initial);


    purchFormLetterParmData.parmOnlyCreateParmUpdate(true);

    purchFormLetterParmData.createData(false);


    purchParmUpdate = purchFormLetterParmData.parmParmUpdate();


    // Populate PurchParmTable

    purchParmTable.clear();


    purchParmTable.TransDate =

        DateTimeUtil::getSystemDate(DateTimeUtil::getUserPreferredTimeZone());


    purchParmTable.DocumentDate =

        DateTimeUtil::getSystemDate(DateTimeUtil::getUserPreferredTimeZone());


    purchParmTable.Ordering              = DocumentStatus::PurchaseOrder;

    purchParmTable.ParmJobStatus         = ParmJobStatus::Waiting;

    purchParmTable.PurchId               = _purchTable.PurchId;

    purchParmTable.PurchName             = _purchTable.PurchName;

    purchParmTable.DeliveryName          = _purchTable.DeliveryName;

    purchParmTable.DeliveryPostalAddress = _purchTable.DeliveryPostalAddress;

    purchParmTable.OrderAccount          = _purchTable.OrderAccount;

    purchParmTable.CurrencyCode          = _purchTable.CurrencyCode;

    purchParmTable.InvoiceAccount        = _purchTable.InvoiceAccount;

    purchParmTable.ParmId                = purchParmUpdate.ParmId;


    purchParmTable.insert();


    // Create Purchase Order confirmation

    purchFormLetter = PurchFormLetter::construct(

        DocumentStatus::PurchaseOrder);


    purchFormLetter.transDate(

        DateTimeUtil::getSystemDate(

            DateTimeUtil::getUserPreferredTimeZone()));


    purchFormLetter.proforma(false);

    purchFormLetter.specQty(PurchUpdate::All);

    purchFormLetter.purchTable(_purchTable);


    purchFormLetter.parmParmTableNum(purchParmTable.ParmId);

    purchFormLetter.parmId(purchParmTable.ParmId);

    purchFormLetter.purchParmUpdate(

        purchFormLetterParmData.parmParmUpdate());


    // Execute the confirmation

    purchFormLetter.run();


    ttsCommit;


    return _purchTable;

}

Friday, 16 January 2026

Get Parent Position Worker Name from Current Worker Position | D365FO X++

In this blog, we explore how to fetch the Parent Position Worker from a Current Worker Position in D365FO.


















The solution revolves around core HCM tables: 
- HcmWorker
- HcmPositionWorkerAssignment
- HcmPosition
- HcmPositionHierarchy.

Source Code:

public Name getParentPositionWorkerName(HcmPositionRecId _positionRecId)
{

    HcmWorker                   hcmWorker;

    HcmPositionWorkerAssignment workerAssignment;

    HcmPositionHierarchy        hcmPositionHierarchy;

    utcdatetime                 now = DateTimeUtil::getSystemDateTime();

 

    // 1. Find the parent position in the hierarchy

    // 2. Join to the assignment table to find who sits in that parent position

    // 3. Join to the worker table to get the name

    select firstonly * from hcmWorker

    join workerAssignment

        where workerAssignment.Worker == hcmWorker.RecId

           && workerAssignment.ValidFrom <= now

           && workerAssignment.ValidTo   >= now

    join hcmPositionHierarchy

        where hcmPositionHierarchy.ParentPosition == workerAssignment.Position

           && hcmPositionHierarchy.Position       == _positionRecId

           && hcmPositionHierarchy.ValidFrom     <= now

           && hcmPositionHierarchy.ValidTo       >= now;

 

    return hcmWorker.name();

}

Monday, 12 January 2026

To understand AI, think in layers

 ðŸš€ Many people think AI progress is about bigger models or new buzzwords.

 But that’s not the real shift.


To understand AI, think in layers:


🔹 Rules-based AI followed instructions written by humans.

 No thinking. No learning.


🔹 Deep Learning learned patterns from data.

 It didn’t know things — it recognized them.


🔹 Generative AI creates content.

 Text, images, code — impressive, but mostly reactive.

 You ask → it responds.


🔹 Agentic AI is different.

 It doesn’t just answer.

 It plans steps, chooses tools, and takes action to reach a goal.


That’s why this matters 👇

 The real breakthrough isn’t what AI can say.

 It’s what AI can do next without being told.


So instead of asking:

 ❌ “Is this the newest model?”


Start asking:

 ✅ “Can this AI think ahead and act on its own?”


That shift — from response to action — is where the next wave of AI is coming from.



Convert Quantity Between Units Using Unit Symbols in D365FO

 In Dynamics 365 Finance & Operations (D365FO) , unit conversion is a common requirement when working with sales, purchasing, inventory,...