<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[The Augmented Architect]]></title><description><![CDATA[Welcome to a specialized technical hub for Dynamics 365 Finance & Operations. This blog moves beyond basic documentation to provide deep-dive X++ tutorials, bat]]></description><link>https://bytebharani.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/69e49f43ee84f66e943a06c9/17ef0c95-3847-4dcf-9ed9-25d6d6b53261.png</url><title>The Augmented Architect</title><link>https://bytebharani.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 24 Jun 2026 16:57:49 GMT</lastBuildDate><atom:link href="https://bytebharani.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Advanced Computed Columns in D365FO with X++: Complex Case Statements]]></title><description><![CDATA[In my previous blog, I have gone through on a general approach how to create a computed method which is so straight forward. The current writing is something more bigger. Lets dig deeper.
Problem:
As ]]></description><link>https://bytebharani.hashnode.dev/advanced-computed-columns-in-d365fo-with-x-complex-case-statements</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/advanced-computed-columns-in-d365fo-with-x-complex-case-statements</guid><category><![CDATA[d365fo]]></category><category><![CDATA[D365 F&O]]></category><category><![CDATA[d365]]></category><category><![CDATA[d365 finance and operations ,]]></category><category><![CDATA[#dynamics365]]></category><category><![CDATA[dynamics365fo]]></category><category><![CDATA[Computed method]]></category><category><![CDATA[complex computed method]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Thu, 14 May 2026 15:07:36 GMT</pubDate><content:encoded><![CDATA[<p>In my previous <a href="https://bytebharani.hashnode.dev/creation-of-a-computed-method-in-a-view-or-in-data-entity-in-d365fo-using-x">blog</a>, I have gone through on a general approach how to create a computed method which is so straight forward. The current writing is something more bigger. Lets dig deeper.</p>
<h3>Problem:</h3>
<p>As the key importance of a computed field is to improve performance and also while exporting from a Data Entity, Skip Staging is the step we do more often for faster export where computed field is still needed instead of Virtual field. The difficulty here is when working on converting a display method to a  computed field which is having many interlinked methods.</p>
<p>On the same note, I recently received a requirement to build an entity for Cost Objects where this form has many display methods and the client always uses this Skip Staging functionality.</p>
<h3>Solution:</h3>
<p>I made similar Entity for physical fields using InventSum, InventDim and InventTableModule tables.</p>
<p>The Average Unit Cost is the field which is basically a display method that took some time to investigate and find the right solution.</p>
<p>And finally made the below Computed Field.</p>
<p>I used a select statement and using limited resources like Item and site with some case and Grouping statements.</p>
<pre><code class="language-java">private static server str getAvgUnitCost()
    {
        str avgUnitCost;
        str postedValue;
        str physicalValue;

        str itemId = SysComputedColumn::returnField(viewStr(InventSumBySiteEntity),
        identifierStr(InventSumBySite),
        fieldStr(InventSumBySite, ItemId));

        str siteId = SysComputedColumn::returnField(viewStr(InventSumBySiteEntity),
        identifierStr(InventSumBySite),
            fieldStr(InventSumBySite, InventSiteId));

        str dataArea = SysComputedColumn::returnField(viewStr(InventSumBySiteEntity),
        identifierStr(InventSumBySite),
            fieldStr(InventSumBySite, InventSumDataAreaId));

        avgUnitCost = strFmt(@'SELECT
                                CASE
                                WHEN (SUM(INVENTSUM.POSTEDQTY) - ABS(SUM(INVENTSUM.DEDUCTED)) + SUM(INVENTSUM.RECEIVED)) IS NULL
                                OR (SUM(INVENTSUM.POSTEDQTY) - ABS(SUM(INVENTSUM.DEDUCTED)) + SUM(INVENTSUM.RECEIVED)) = 0
                                THEN 0.0
                                ELSE
                                ABS(
                                (SUM(INVENTSUM.POSTEDVALUE) + SUM(INVENTSUM.PHYSICALVALUE)) /
                                (SUM(INVENTSUM.POSTEDQTY) - ABS(SUM(INVENTSUM.DEDUCTED)) + SUM(INVENTSUM.RECEIVED))
                                )
                                END AS AVGUNITCOST
                                FROM INVENTSUM
                                WHERE INVENTSUM.ITEMID = %1
                                AND INVENTSUM.INVENTSITEID = %2
                                AND INVENTSUM.DATAAREAID = %3
                                GROUP BY INVENTSUM.ITEMID, INVENTSUM.INVENTSITEID', itemId, siteId, dataArea);
        return avgUnitCost;
    }
</code></pre>
<p>The idea here is basically to give the Average Unit Cost of that item in that Site, which is equal to total sum of PostedValue and PhysicalValue which is divided by sum of Received Quantity and difference of Posted and Deducted Quantity.</p>
<p>If we have PhysicalValue, PostedValue or Deducted values then we can leverage SysComputedColumn Class methods.</p>
<p>Below is the implementation for the same</p>
<pre><code class="language-java">private static server str getAvgUnitCost()
    {
        str avgUnitCost;

        str postedVal = SysComputedColumn::returnField(viewStr(InventWarehouseOnHandV2Entity),
        identifierStr(InventWarehouseOnHandAggregatedView),
        fieldStr(InventWarehouseOnHandAggregatedView, PostedValue));

        str postedQty = SysComputedColumn::returnField(viewStr(InventWarehouseOnHandV2Entity),
        identifierStr(InventWarehouseOnHandAggregatedView),
        fieldStr(InventWarehouseOnHandAggregatedView, PostedQty));

        str physicalVal = SysComputedColumn::returnField(viewStr(InventWarehouseOnHandV2Entity),
        identifierStr(InventWarehouseOnHandAggregatedView),
            fieldStr(InventWarehouseOnHandAggregatedView, PhysicalValue));

        str deducted = SysComputedColumn::returnField(viewStr(InventWarehouseOnHandV2Entity),
        identifierStr(InventWarehouseOnHandAggregatedView),
            fieldStr(InventWarehouseOnHandAggregatedView, Deducted));

        str received = SysComputedColumn::returnField(viewStr(InventWarehouseOnHandV2Entity),
        identifierStr(InventWarehouseOnHandAggregatedView),
            fieldStr(InventWarehouseOnHandAggregatedView, Received));

        str totalQtyExp = strFmt('%1 - ABS(%2) + %3', postedQty, deducted, received);
        str totalValue = strFmt('%1 + %2', postedVal, physicalVal);


        avgUnitCost = SysComputedColumn::if(
            SysComputedColumn::equalExpression(totalQtyExp, SysComputedColumn::returnLiteral(0)),   // Checking if totalQtyExp is 0
            SysComputedColumn::returnLiteral(0),					                                // If 0 then return 0
            SysComputedColumn::abs(SysComputedColumn::divide(totalValue, totalQtyExp))              // Else divide both i.e., totalValue and totalQtyExp
            );

        return avgUnitCost;
    }
</code></pre>
<p>The actual display method can be seen at InventCostOnhandItem Form and at InventSum datasource methods with name averageCostPriceUnit(). From the UI, this can be obtained from Released Products &gt; Manage Costs &gt; Cost Objects &gt; Average Unit Cost.</p>
<p>With this we have seen some tricky Switch statement in SQL and then used that in our X++ to write a computed method.</p>
<p>Happy Learning!</p>
<p>PS: The Entity or table names looks like standard but they aren't standard. I just used these names for demo purpose.</p>
]]></content:encoded></item><item><title><![CDATA[Logic to get all Dimension Names and Values from Default Dimensions - D365FO X++]]></title><description><![CDATA[Sometimes we need to retrieve all the dimensions from the dimension reference field which we have in our tables.
Here is a generic logic which gets all the dimensions for the particular order or may b]]></description><link>https://bytebharani.hashnode.dev/dimension-names-and-values-from-default-dimensions-d365fo-x</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/dimension-names-and-values-from-default-dimensions-d365fo-x</guid><category><![CDATA[d365fo]]></category><category><![CDATA[D365 F&O]]></category><category><![CDATA[d365]]></category><category><![CDATA[d365 finance and operations ,]]></category><category><![CDATA[#dynamics365]]></category><category><![CDATA[dynamics 365 finance]]></category><category><![CDATA[dynamics365fo]]></category><category><![CDATA[default dimension]]></category><category><![CDATA[dimension name]]></category><category><![CDATA[dimension value]]></category><category><![CDATA[get dimension from default dimension]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Thu, 14 May 2026 14:58:55 GMT</pubDate><content:encoded><![CDATA[<p>Sometimes we need to retrieve all the dimensions from the dimension reference field which we have in our tables.</p>
<p>Here is a generic logic which gets all the dimensions for the particular order or may be any table. Lets say Sales Order or Customers or Vendors table or some Journal related tables. <br />This simple method needs the default dimension as the parameter and returns us Dimension Names and its Values.</p>
<p>Currently the dimNames and dimValues are the container type variables defined globally in class as I have a different requirement.</p>
<pre><code class="language-ruby">private void getDimensions(DimensionDefault _defaultDimension)
{
    DimensionAttributeValueSetItem  setItem;
    DimensionAttributeValue         dimAttrValue;
    DimensionAttribute dimAttr;

    dimNames = conNull();
    dimValues = conNull();

    while select DisplayValue from setItem
        where setItem.DimensionAttributeValueSet == _defaultDimension
        join dimAttrValue
            where dimAttrValue.RecId == setItem.DimensionAttributeValue &amp;&amp;
            dimAttrValue.IsDeleted == false
        join Name from dimAttr
        where dimAttr.RecId == dimAttrValue.DimensionAttribute
    {
        dimNames += dimAttr.Name;
        dimValues += setItem.DisplayValue;
    }
}
</code></pre>
<p><strong>Happy Learning!</strong></p>
]]></content:encoded></item><item><title><![CDATA[SysDa Update in X++ D365FO Part II]]></title><description><![CDATA[In continuation to my older article on SysDa API for inserting records, here we go with my new article on updating table with some joins and complex syntaxes of SysDa involved.
Lets not wait more!
Use]]></description><link>https://bytebharani.hashnode.dev/sysda-update-in-x-d365fo-part-ii</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/sysda-update-in-x-d365fo-part-ii</guid><category><![CDATA[d365fo]]></category><category><![CDATA[d365]]></category><category><![CDATA[D365 F&O]]></category><category><![CDATA[d365 finance and operations ,]]></category><category><![CDATA[dynamics365fo]]></category><category><![CDATA[sysda]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Thu, 14 May 2026 14:52:01 GMT</pubDate><content:encoded><![CDATA[<p>In continuation to my older <a href="https://bytebharani.hashnode.dev/sysda-insert-in-d365fo-part-i">article</a> on SysDa API for inserting records, here we go with my new article on updating table with some joins and complex syntaxes of SysDa involved.</p>
<p>Lets not wait more!</p>
<h3>Use Case:</h3>
<p>In a scenario where performance is considered too critical, then we need to go with this.</p>
<h3>Scenario:</h3>
<p>We are going to update a field of a table. This can be done using update_recordset or Query::update_recordset.</p>
<p>The current approach is much more faster than the above 2.</p>
<p>Lets say we have header and line tables, perhaps we can take as an example of SalesQuotationTable and SalesQuotationLine. We have some status on Line level.</p>
<p>So if the current date is equal to the Expiry date of SalesQuotationHeader, we are going to mark the Line with some status like lets say stop.</p>
<p>So now, lets see the code and we will understand.</p>
<pre><code class="language-csharp">//Table buffers declaration
SalesQuotationTable salesQuotationTable;
SalesQuotationLine salesQuotationLine;

//Getting and setting the current date to Expiry date Variable

TransDate expiryDate = DateTimeUtil::getSystemDate(DateTimeUtil::getUserPreferredTimeZone());


//Creating Update Object on SalesQuotationLine which generates an SQL Statement same like update_recordset tableName

var updateObject = new SysDaUpdateObject(salesQuotationLine);

//The below statement is like assigning a value or to the field like SET fieldName = "some Value"

updateObject.settingClause()
     .add(fieldStr(SalesQuotationLine, SQStatus)
     ,new SysDaValueExpression(SQStatus::Stop));

//WHERE Condition along with AND

queryObject.whereClause(
      new SysDaEqualsExpression(
          new SysDaFieldExpression(salesQuotationTable, fieldStr(SalesQuotationTable, QuotationId)),
          new SysDaFieldExpression(salesQuotationLine, fieldStr(SalesQuotationLine, QuotationId)))
           .and(
      new SysDaEqualsExpression(
          new SysDaFieldExpression(salesQuotationTable, fieldStr(SalesQuotationTable, QuotationExpiryDate)),
          new SysDaValueExpression(expiryDate))));


//JOIN

updateObject.joinClause(SysDaJoinKind::ExistsJoin, queryObject);


//Update Operation Final Execution

ttsbegin;
new SysDaUpdateStatement().update(updateObject);
​​​​​​​ttscommit;
</code></pre>
<h3>Code Explanation:</h3>
<p>As you guys already know, I'm updating a field using SysDa API.</p>
<p>Using <strong>SysDaUpdateObject</strong>, I told the system that we are going to update this table. And then assigned some value which we can say as "<strong>STOP</strong>". Then we are having a where clause, which seems for visibility, it  tricky but it is not in actual.</p>
<p>In the <strong>WHERE</strong> clause, I'm writing a general relation between SalesQuotationLine and SalesQuotationTable and then adding another condition with <strong>AND</strong>, checking if the Expiry Date at Header is actually equal to the current date which we found at the start.</p>
<p>And then I gave a join clause since I'm doing a join with Header table and made a join of <strong>Exists</strong> <strong>Join</strong> as you all know that I am a big fan of this join :)</p>
<p>Finally in a transaction, I called the actual update by passing the updating object as a parameter.</p>
<p>​​​The syntax is bit time taking to write in actual but this gives a huge boost to the total performance.</p>
<p>That's all about Update Operation with Joins using SysDa.<br />Stay Tuned!</p>
<p>Happy Learning :p</p>
]]></content:encoded></item><item><title><![CDATA[Convert Epoch to utcdatetime in D365FO using X++]]></title><description><![CDATA[While working with integrations with some 3rd party application to FO, we might have scenarios where we get the current timestamp in Epoch/Unix Format. And we may need to flow that value to FO as a da]]></description><link>https://bytebharani.hashnode.dev/convert-epoch-to-utcdatetime</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/convert-epoch-to-utcdatetime</guid><category><![CDATA[d365fo]]></category><category><![CDATA[d365]]></category><category><![CDATA[d365 finance and operations ,]]></category><category><![CDATA[#dynamics365]]></category><category><![CDATA[dynamics365fo]]></category><category><![CDATA[epoch]]></category><category><![CDATA[utcdatetime]]></category><category><![CDATA[time convertion]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Thu, 14 May 2026 14:45:46 GMT</pubDate><content:encoded><![CDATA[<p>While working with integrations with some 3rd party application to FO, we might have scenarios where we get the current timestamp in Epoch/Unix Format. And we may need to flow that value to FO as a date time or just date. In that case, to use that in FO, we need to do some conversion.</p>
<p>We have a simple logic which we can use for this conversion.</p>
<p>Let's say we are getting the epoch value as a string in our payload. Then we can convert first that into real and pass the value into below method and then this method returns the final output in utcdatetime format.</p>
<pre><code class="language-csharp">//The field on LHS is a utcDateTime field.

this.PaymentDateTimeConverted = InvoiceUtility::convertUnixOrEpochTimeStampToDateTime(any2Real(this.PaymDateTime));


public static utcdatetime convertUnixTimeStampToDateTime(real _seconds)
{
    System.DateTime dtDateTime = new System.DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind::Utc);
        
    dtDateTime = dtDateTime.AddSeconds(_seconds);

    // The below method converts DateTime to UtcDateTime
    return clrSystemDateTime2UtcDateTime(dtDateTime);
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Allow Edit of a specific field and to disable complete Form Datasource]]></title><description><![CDATA[Recently I came across a requirement in which I need to disable complete datasource but make a single field editable.
In this scenario, the traditional way of making datasource disable by using allowe]]></description><link>https://bytebharani.hashnode.dev/allow-edit-of-a-specific-field-and-to-disable-complete-form-datasource</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/allow-edit-of-a-specific-field-and-to-disable-complete-form-datasource</guid><category><![CDATA[d365]]></category><category><![CDATA[D365 F&O]]></category><category><![CDATA[d365fo]]></category><category><![CDATA[d365 finance and operations ,]]></category><category><![CDATA[#dynamics365]]></category><category><![CDATA[dynamics365fo]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Thu, 14 May 2026 14:40:00 GMT</pubDate><content:encoded><![CDATA[<p>Recently I came across a requirement in which I need to disable complete datasource but make a single field editable.</p>
<p>In this scenario, the traditional way of making datasource disable by using <strong>allowedit(false)</strong> and enabling a field will not work since allowedit() method will disable complete datasource. Even we cannot make just the control enabled since whole datasource is disabled.</p>
<p>The best way here is to disable each and every field and enable the specific field.</p>
<p>We no need to write disabling logic for every field nor to write some loop here to loop through all the fields of table and disable.</p>
<h3>The Method:</h3>
<p>We can use one standard method which serves our purpose. Below is the method for the same.</p>
<p><strong>allowEditFieldsOnFormDS_W(, )</strong></p>
<p>This method just requires the <strong>FormDataSource</strong> as a parameter and a <strong>boolean</strong> parameter to allow editing.</p>
<h3>Understanding of method:</h3>
<pre><code class="language-csharp">public static void allowEditFieldsOnFormDS_W(FormDataSource _dataSource, boolean _allowEdit)
    {
        var dictTable = new DictTable(_dataSource.table());
        int         cx, idx;

        for (cx = 1; cx &lt;= dictTable.fieldCnt(); cx ++)
        {
            DictField dictField = dictTable.fieldObject(dictTable.fieldCnt2Id(cx));

            if (! dictField.isSystem())
            {
                for (idx = 1; idx &lt;= dictField.arraySize(); idx++)
                {
                    FormDataObject dataObject = _dataSource.object(fieldId2Ext(dictField.id(), idx));
                    if (dataObject)
                    {
                        dataObject.allowEdit(_allowEdit);
                    }
                }
            }
        }
    }
</code></pre>
<p>This method as I said above takes, <strong>FormDataSource</strong> and a <strong>boolean</strong> as a parameter and based on the field count, this loops through all the fields and if it is not a system field like RecId or Partition, then it gets the <strong>FormDataObject</strong> from field and sets the form datasource field's allow edit property. This method is found in Global class.</p>
<p>This also works, let's say if you have a requirement to disable the complete SalesLine and some warehouse related or other datasource related fields should be enabled, then also allowedit() method will not work. This works in that scenario.</p>
<h3>Implementation:</h3>
<p>We just need to call this method with required parameters and then whichever field we need to be editable, make that field to be editable. Below is an example.</p>
<pre><code class="language-csharp">allowEditFieldsOnFormDS_W(dirPartyTable_ds, false);
      dirPartyTable_ds.object(fieldNum(DirPartyTable, KnownAs)).
           allowEdit(true);
</code></pre>
<p>Here we are making the DirPartyTable Form Datasource to be disabled and making KnownAs field editable.</p>
<p>Thats all for now!</p>
<p>Happy Learning!</p>
]]></content:encoded></item><item><title><![CDATA[Enable or Disable Visibility of a Menu Item in D365FO using X++]]></title><description><![CDATA[We generally disable or make the control directly invisible in any form using some standard methods like form datasource active() or may be init() or using some methods in interactive classes if using]]></description><link>https://bytebharani.hashnode.dev/control-menu-item-visibility-d365fo</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/control-menu-item-visibility-d365fo</guid><category><![CDATA[d365fo]]></category><category><![CDATA[d365]]></category><category><![CDATA[D365 F&O]]></category><category><![CDATA[d365 finance and operations ,]]></category><category><![CDATA[dynamics365fo]]></category><category><![CDATA[Menu Item]]></category><category><![CDATA[Control Visibility]]></category><category><![CDATA[Enable Menu Item]]></category><category><![CDATA[Disable Menu Item]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Thu, 14 May 2026 14:32:20 GMT</pubDate><content:encoded><![CDATA[<p>We generally disable or make the control directly invisible in any form using some standard methods like form datasource active() or may be init() or using some methods in interactive classes if using a list page. But let's say we have a requirement to make a menuitem invisible based on certain condition. Then how to achieve it? Here we do not have any form method, right?</p>
<p>So, let's not wait more and check how this can be done.</p>
<p>To achieve the above requirement, we need to subscribe to a standard delegate called <strong>checkAddSubMenuDelegate</strong> of Class <strong>SysMenuNavigationObjectFactory,</strong></p>
<p>Use case here is if the Menu Item should be invisible in some specific legal entity or may be in other condition, we can follow this.</p>
<p>Below is the approach for the same. The condition returns a boolean value which specifies if the menuitem should be visible or invisible.</p>
<pre><code class="language-csharp">[SubscribesTo(classstr(SysMenuNavigationObjectFactory), staticdelegatestr(SysMenuNavigationObjectFactory, checkAddSubMenuDelegate))]
    public static void menuItemVisibilityHandler(SysDictMenu _rootMenu, SysDictMenu _subMenu, SysBoxedBoolean _subMenuVisibility)
    {
        if (_subMenu.isMenuItem())   //Checking if it is a menuitem
        {
            var metaElement = _subMenu.GetMenuItemMetaElement();
            if (metaElement != null)
            {
                switch (metaElement.Name)  //Checking the menuitem name and adding condition
                {
                    case menuItemActionStr(MenuItemOne):
                        _subMenuVisibility.value = /&lt;Condition to specify&gt;/;
                        break;
                    case menuItemDisplayStr(MenuItemTwo):
                        _subMenuVisibility.value = /&lt;Condition to specify&gt;/;
                        break;
                }
            }
        }
    }
</code></pre>
<p>In this way we can achieve this.</p>
<p>Happy Learning :)</p>
]]></content:encoded></item><item><title><![CDATA[Creation of a Computed Method in a View or in Data Entity in D365FO using X++]]></title><description><![CDATA[Today we will learn about computed methods in a View or a Data Entity.
As the name suggests the computed methods are computed in SQL level and adds the query directly to the View/Data entity's metadat]]></description><link>https://bytebharani.hashnode.dev/creation-of-a-computed-method-in-a-view-or-in-data-entity-in-d365fo-using-x</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/creation-of-a-computed-method-in-a-view-or-in-data-entity-in-d365fo-using-x</guid><category><![CDATA[d365fo]]></category><category><![CDATA[D365 F&O]]></category><category><![CDATA[d365 finance and operations ,]]></category><category><![CDATA[dynamics365fo]]></category><category><![CDATA[Computed method]]></category><category><![CDATA[Data Entity]]></category><category><![CDATA[view]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Thu, 14 May 2026 14:18:50 GMT</pubDate><content:encoded><![CDATA[<p>Today we will learn about computed methods in a View or a Data Entity.</p>
<p>As the name suggests the computed methods are computed in SQL level and adds the query directly to the View/Data entity's metadata. A computed method generates/returns a SQL query which is directly added to the View metadata.</p>
<p>This doesn't mean that whenever a view is executed, the computed method executes for each line. The computed method executes when the view is loaded and in SQL level. So that if any unmapped field is using this, then we can run a select in SSMS to check that field's value. This is completely different with the normal display method which is executed at runtime in X++. So that's why whenever we need to use display methods, for better performance, this is one of the ways to do instead of a display method.</p>
<p>So let us understand how to write a computed method or a computed column or a SysComputedColumn method.</p>
<pre><code class="language-ruby">public static server str getVendorAccountNum()
{
    str vendorAccount;

    str voucherNum = SysComputedColumn::returnField(viewStr(OURVIEWNAME),   //Pass View name in 'OURVIEWNAME'
        identifierStr(GeneralJournalEntry),                                 //Pass DataSource name
        fieldStr(GeneralJournalEntry, SubLedgerVoucher));                   //Pass the field name on which we are interested

    str dataArea = SysComputedColumn::returnField(viewStr(OURVIEWNAME),
        identifierStr(GeneralJournalEntry),
        fieldStr(GeneralJournalEntry, SubLedgerVoucherDataAreaId));


    vendorAccount = strFmt(@'SELECT TOP 1
                                  VENDTRANS.ACCOUNTNUM
                             FROM VENDTRANS
                             WHERE
                                  VENDTRANS.VOUCHER = %1
                                  AND VENDTRANS.DATAAREAID = %2,
                             voucherNum,
                             dataArea');

    return vendorAccount;

}
</code></pre>
<p>Above is a simple example on how to write a computed method. Many complex queries can be written this way.</p>
<p>A computed method returns a SQL query in string format which is then used by any field in that view/entity. Irrespective of the datatype, the computed method's return type is always a string. So, from above we are fetching the vendor account from a computed method. </p>
<p>For creating a computed field, an unmapped field should be created in the view and make sure that computed column property is enabled at the properties level and add this method as a view method.</p>
<p>In entities also where we have read-only fields and if data is being populated from postload() method, then using a computed column instead improves the performance a lot.</p>
<p>This way we can create a computed method.</p>
]]></content:encoded></item><item><title><![CDATA[Opening a form based on user filters in D365FO Using X++ and Query Classes]]></title><description><![CDATA[If let's say there is a requirement to show a filter query where user can add different ranges he wants and once he clicks 'Ok', based on the query selected by user, the form should render. Or let's s]]></description><link>https://bytebharani.hashnode.dev/opening-a-form-based-on-user-filters-in-d365fo-using-x-and-query-classes</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/opening-a-form-based-on-user-filters-in-d365fo-using-x-and-query-classes</guid><category><![CDATA[d365fo]]></category><category><![CDATA[d365]]></category><category><![CDATA[D365 F&O]]></category><category><![CDATA[d365 finance and operations ,]]></category><category><![CDATA[dynamics365fo]]></category><category><![CDATA[Query]]></category><category><![CDATA[query-optimization]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Thu, 14 May 2026 14:11:56 GMT</pubDate><content:encoded><![CDATA[<p>If let's say there is a requirement to show a filter query where user can add different ranges he wants and once he clicks 'Ok', based on the query selected by user, the form should render. Or let's say we need to show the filter query button which will be same as the 'Records to include' in SSRS reports. on a form so when user clicks the button then it will show a form where should be able to add ranges.</p>
<p> So, how is this possible by X++ and Query classes?</p>
<p> Let's not wait and will check the plan.</p>
<pre><code class="language-ruby">public void init()
    {
        super();

        Query query = new Query();
        SysQueryRun queryRun;

        query = CustInvoiceTrans_ds.query();
        queryRun = new SysQueryRun(new Query(query));

        queryRun.args().caller(this);
        queryRun.title("Invoice Journal Report");

        queryRun.args().allowUseOfPreloadedForm(false);
        queryRun.promptAllowAddRange(QueryAllowAdd::NoFields);

        if (queryRun.prompt())
        {
            CustInvoiceTrans_ds.query(queryRun.query());
        }

        else
        {
            element.close();
        }
        
    }
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Copy Records between two different Tables having same table schema in D365FO using X++]]></title><description><![CDATA[What is this copying of record from table to another which I am talking about? Where this is used? This can be simply achieved by assigning value right? But why this generic method? 
To answer all the]]></description><link>https://bytebharani.hashnode.dev/copy-records-between-two-different-tables</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/copy-records-between-two-different-tables</guid><category><![CDATA[d365fo]]></category><category><![CDATA[d365]]></category><category><![CDATA[D365 F&O]]></category><category><![CDATA[d365 finance and operations ,]]></category><category><![CDATA[dynamics365fo]]></category><category><![CDATA[copydata]]></category><category><![CDATA[table]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Thu, 14 May 2026 14:02:55 GMT</pubDate><content:encoded><![CDATA[<p>What is this copying of record from table to another which I am talking about? Where this is used? This can be simply achieved by assigning value right? But why this generic method? </p>
<p>To answer all these questions, lets read this :)</p>
<p> Lets says we need to insert or update the data from one table to another table where the two tables are completely different tables. Ideally we will just assign each and every field value from source to target. But lets say we have many fields. Like we cant just assign each and every field with those many fields and we have to use this in multiple places in the code. Then in general we will use buf2buf or data() methods to perform this activity.</p>
<p>But I have a different requirement where I need to copy all data from one table to another having same fields but 2 different tables.</p>
<p> This particular scenario exists when you have some log table or else some custom workflow related tables.</p>
<p>To accomplish this task what I did is I customized the standard as per my requirement so this can now work with 2 different tables. Basically the buf2buf requires same table as the source and target. But here I made this work with 2 different tables using DictTable.</p>
<pre><code class="language-ruby">private void insertOrUpdateData(Common _source, Common _target)
{
	var         dictTable           = new DictTable(_source.TableId);
    DictTable   dicttableTarget     = new DictTable(_target.TableId);
    FieldId     fieldId             = dictTable.fieldNext(0);

    while (fieldId &amp;&amp; ! isSysId(fieldId))
    {
        FieldId     fieldIdTarget;

        fieldIdTarget               = dicttableTarget.fieldName2Id(dictTable.fieldObject(fieldId).name());

        if (fieldIdTarget)
        {
            _target.(fieldIdTarget)     = _source.(fieldId);
        }
        else
        {
            fieldId                     = dictTable.fieldNext(fieldId);
            continue;
        }
        fieldId                     = dictTable.fieldNext(fieldId);
    }
}
</code></pre>
<p>This method can be called while insert or update and it needs the source and target tables to be passed as parameters.</p>
<p>Lets say may be you do not want to copy some field. So you can simply say the system not do copy for this field by below.</p>
<pre><code class="language-ruby">FieldId  fieldId     = dictTable.fieldNext(0);
const str Party      = "Party";

boolean ignoreThisField = dictTable.fieldObject(fieldId).name() != Party;
</code></pre>
<p>Here we are checking the field name using fieldId and saying the system if to include it or no.</p>
<p>The reason why we has this line of code, isSysId(fieldId) is this will skip the standard fields like RecId, RecVersion.</p>
<p>This method can be called while insert or update and after that based on process need to call the insert() or update() method something like below.</p>
<pre><code class="language-java">TableNameSource tableNameSource;
TableNameTarget tableNameTarget;

//insert();
//call the method and pass arguments
if (tableNameTarget.validatewrite())
{
      tableNameTarget.insert();
}

//update
//selecting which record to update
if (tableNameTarget)
{
       tableNameTarget.selectforupdate(true);
       //call method and pass arguments

       if (tableNameTarget.validatewrite())
       {
           ttsbegin;
           tableNameTarget.update();
           ttscommit;
       }
}
</code></pre>
<p>With this approach data can be copied between 2 different tables having same Field names.</p>
]]></content:encoded></item><item><title><![CDATA[Using Temporary table as a datasource for Lookup in Forms]]></title><description><![CDATA[Temporary table as a lookup datasource
We may sometimes encounter a situation where we need to use a temporary table as a datasource in a form lookup. But this is a bit tricky because it gets data on ]]></description><link>https://bytebharani.hashnode.dev/temporary-table-as-a-datasource-for-lookup-in-forms</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/temporary-table-as-a-datasource-for-lookup-in-forms</guid><category><![CDATA[Temporary Table]]></category><category><![CDATA[lookup]]></category><category><![CDATA[forms]]></category><category><![CDATA[d365fo]]></category><category><![CDATA[d365]]></category><category><![CDATA[dynamics 365 finance]]></category><category><![CDATA[dynamics365fo]]></category><category><![CDATA[customization]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Thu, 14 May 2026 13:53:21 GMT</pubDate><content:encoded><![CDATA[<h3>Temporary table as a lookup datasource</h3>
<p>We may sometimes encounter a situation where we need to use a temporary table as a datasource in a form lookup. But this is a bit tricky because it gets data on runtime, and we can't just do it in normal way like how we do for regular tables. So, this requires runtime insert of data as a temporary table doesn't contain data stored and should trigger lookup.</p>
<h3>Business Scenario:</h3>
<p>Let's say we have a requirement where we have some constant variables which are defined in a class as constants, and we need to give them as a lookup instead of hard coding from UI by user. As those are not enums and defined in class what I did is I inserted them to a temporary table called Inmemory table here and did the further coding of lookup. Main important thing here in we need to pass the temporary table buffer into SysTableLookup objects by using parmTmpBuffer() method.</p>
<p>Here is the code for the same. </p>
<pre><code class="language-ruby">public void lookup(Object _object)
{
    EmailFieldsTmp  temp;

    temp.EmailFieldName = SendEmail::CustomerName;
    temp.insert();

    temp.EmailFieldName = SendEmail::CustomerManagerName;
    temp.insert();
  
    SysTableLookup              sysTableLookup = SysTableLookup::newParameters(tablenum(EmailFieldsTmp), _object);
    Query                       query = new Query();
    QueryBuildDataSource        queryBuildDataSource;

    queryBuildDataSource = query.addDataSource(tablenum(EmailFieldsTmp));
    sysTableLookup.addLookupfield(fieldnum(EmailFieldsTmp, EmailFieldName), true);
 
    sysTableLookup.parmQuery(query);
    sysTableLookup.parmTmpBuffer(temp);
    sysTableLookup.performFormLookup();
}
</code></pre>
<p>Here I just added some randomized constants with some values which will be shown for lookup. But in general if we have some set of data, we can loop and show that and assign that to SysTableLookup object. In this way this is achievable.</p>
<p>Please post for any questions and happy learning!</p>
]]></content:encoded></item><item><title><![CDATA[Multi Selection on a form by user and get selected records by X++ code]]></title><description><![CDATA[Context
We might have scenario where we need to customize a form to add a button so that it should have multiselect capability and for example some functionality should happen on multi select like to ]]></description><link>https://bytebharani.hashnode.dev/multiple-records-selection-on-form-by-user</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/multiple-records-selection-on-form-by-user</guid><category><![CDATA[d365fo]]></category><category><![CDATA[D365 F&O]]></category><category><![CDATA[#dynamics365]]></category><category><![CDATA[dynamics365fo]]></category><category><![CDATA[Multi selection]]></category><category><![CDATA[forms]]></category><category><![CDATA[d365 finance and operations ,]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Wed, 29 Apr 2026 16:28:55 GMT</pubDate><content:encoded><![CDATA[<h3>Context</h3>
<p>We might have scenario where we need to customize a form to add a button so that it should have multiselect capability and for example some functionality should happen on multi select like to get all selected records by user so that scenario is to send them as a list to some external service for integration.</p>
<p>Or we might also have a scenario so that for example if we are generating packing slip, then we only have to select all Saleslines with same warehouse. In such scenarios also we will go by multi select.</p>
<p>Ideal way would be to create a button by setting multi select property to yes.</p>
<p>But question is how to get all selected records in a form by code?</p>
<p>What are the best ways to do?</p>
<p>This can be done by either event handler like onclicked event handler or the pre coc on clicked method of the button.</p>
<p>Basically, we can do this by MultiSelectionHelper class. But we also have another way.</p>
<p>So how to do that then?</p>
<p>For this we need to get the formdatasource from the FormControl class and FormDataSource class as shown in the below codes.</p>
<p>When I have above requirements for the first time, I have written my code using while loop. For the Sales order line validation, I choose the For loop. Comparatively For loop is faster.</p>
<p>To not to change the basic theme of this blog, I will only explain how to do multiselect via X .</p>
<p>Below code gives the idea how to get the selected records on a form.</p>
<p>Here the main idea is to get the formdatasource and from that we need to assign that buffer to the Table buffer.</p>
<p>The buffer will increment with new record and shows all the records selected by the user.</p>
<h3>With While Loop:</h3>
<pre><code class="language-ruby">FormDateSource salesLine_DS;
SalesLine salesLine;

salesLine = salesLine_DS.getFirst(1) ? salesLine_DS.getFirst(1) : salesLine_DS.cursor();

while (salesLine)
{
  info(salesLine.LineNumber);
  salesLine = salesLine_DS.getNext();
}
</code></pre>
<h3>With For Loop:</h3>
<pre><code class="language-ruby">[ExtensionOf(formControlStr(SalesTable, buttonUpdateInvoice))] 
final class MYSalesTable_buttonUpdateInvoice_Extension 
{
  public void clicked() 
  {
    SalesLine salesLine;
    FormControl formButtonControl = any2Object(this) as FormControl;
    FormDataSource salesLine_ds = formButtonControl.formRun().dataSource(tableStr(SalesLine));
    for (salesLine = salesLine_ds.getFirst(1); salesLine; salesLine = salesLine_ds.getNext()) {
      //Here you can test the working by adding some unique ID. For example Sales Id and line Number in a info log.
    }
    next clicked();
  }
}
</code></pre>
<p>This article is originally posted <a href="https://community.dynamics.com/blogs/post/?postid=7a33810e-28ca-4fdb-9d27-2ec494021c80">here</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Optimizing the D365FO & AX Database performance using RecordInsertList]]></title><description><![CDATA[Scenario:
As we work with data, we usually have a database. So, all the actions which we perform for customization should be in such a way that it should not impact the performance of the system. We s]]></description><link>https://bytebharani.hashnode.dev/performance-optimization-in-d365fo-by-recordinsertlist</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/performance-optimization-in-d365fo-by-recordinsertlist</guid><category><![CDATA[d365fo]]></category><category><![CDATA[D365 F&O]]></category><category><![CDATA[#dynamics365]]></category><category><![CDATA[dynamics365fo]]></category><category><![CDATA[optimization]]></category><category><![CDATA[performance]]></category><category><![CDATA[Performance Optimization]]></category><category><![CDATA[clean code]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Wed, 29 Apr 2026 16:16:16 GMT</pubDate><content:encoded><![CDATA[<h3>Scenario:</h3>
<p>As we work with data, we usually have a database. So, all the actions which we perform for customization should be in such a way that it should not impact the performance of the system. We should be very careful while writing any piece of code.</p>
<p>For example, if we are working on SSRS reports and if the data retrieved by report is huge. Let’s assume the report is a DP based, where we will have a query which accepts inputs from the user and retrieve the data as a report. Generally speaking, user wants the report to run within no time even the report has huge data. If it takes huge time to run, it would be frustrating right?</p>
<p>As a developer we can understand the scenario if it is a query where user gives input, then we will have the X Query classes like QueryRun, Query etc. We generally write the code here like below to get the data from the user input.</p>
<p>Let's assume I have a "query" which sets based on user input.</p>
<pre><code class="language-ruby">InventTable             inventTable;
InventTrans             inventTrans;
QueryRun                queryRun;
RecordInsertList        myTableTmpList;
MyTable                 myTable;

queryRun            = new QueryRun(query);

while(queryRun.next())
{
    
    inventTable = queryRun.get(tableNum(InventTable));
    inventTrans = queryRun.get(tableNum(InventTrans));
    
    myTable.clear();
    myTable.ItemId = inventTable.ItemId;
    myTable.PostedDate = inventTrans.DatePhysical;
    myTable.ItemCreatedDate  = any2Date(inventTable.CreatedDateTime);
    myTable.insert();

}   
</code></pre>
<p>This code looks good and perfect, but it will call the database for each insert which will actually impact the report performance. I mean if it has 10000 lines, it will call database for each insert. Right?</p>
<p>So, how to tackle this?</p>
<p>Also, in this particular scenario we only need to use Query builder classes which are actually slower than the normal select statement in X but performance should be improved. Also, we cannot use insert_recordset here. So, is there any way to call the database only once with all the data to insert?</p>
<h3>Solution:</h3>
<p>One best way is to use Query builder classes along with RecordInsertList.</p>
<p>So, what is RecordInsertList?</p>
<p>As the name suggests it makes all the records which are retrieved by while or query builder into a list and finally calls the database once for the bulk insert of data. In this way we can speed up the performance of database. Simply a kind of List class.</p>
<p>Below is the code which explains the functionality of this using only a while loop.</p>
<p>Just like a normal while select, we will select all the records where the invoice date range is between given from and to date. So whatever data is retrieved it is added as an element into list and finally the list will call the insertDatabase() method which will insert all the listed data at once to the table.</p>
<pre><code class="language-ruby">class testRecordInsertList
{
    public static void main(Args  _args)
    {
        ProjInvoiceJour         projInvoiceJour;
        RecordInsertList        myTableTmpList;
        MyTable                 myTable;
        
        //initiating the list with temp table buffer
        myTableTmpList = new RecordInsertList(tableNum(MyTable), false, false, false, false, false, myTable);
            
        while select InvoiceDate InvoiceId from projInvoiceJour 
            where projInvoiceJour.InvoiceDate &gt;= _fromdate
                &amp;&amp; projInvoiceJour.InvoiceDate &lt;= _todate
            {
                myTable.InvDate = projInvoiceJour.InvoiceDate;
 	            myTable.Invoice = projInvoiceJour.InvoiceId;
                myTableTmpList.add(myTable);
            }
        myTableTmpList.insertDatabase();
    }
}
</code></pre>
<p>So, with this all the data is retrieved and made as a list for each record and finally will call database for insertion of all records as a list.</p>
<p>Below is the code where I am using Query classes with while.</p>
<p>Let’s assume that I have a complex query called “query” which sets based on user input. For demonstration purpose I only used 2 tables for the query.</p>
<p>The code will be as below.</p>
<pre><code class="language-ruby">InventTable             inventTable;
InventTrans             inventTrans;
QueryRun                queryRun;
RecordInsertList        myTableTmpList;
MyTable                 myTable;

queryRun            = new QueryRun(query);
myTableTmpList      = new RecordInsertList(MyTable), false, false, false, false, 
                    false, myTable);        

while(queryRun.next())
{
    
    inventTable = queryRun.get(tableNum(InventTable));
    inventTrans = queryRun.get(tableNum(InventTrans));
    
    myTable.clear();
    myTable.ItemId = inventTable.ItemId;
    myTable.PostedDate = inventTrans.DatePhysical;
    myTable.ItemCreatedDate  = any2Date(inventTable.CreatedDateTime);
    myTableTmpList.add(myTable);

}   
myTableTmpList.insertDatabase();
</code></pre>
<p>When compared to speed between normal insert() and RecordInsertList, there will be huge difference. RecordInsertList is much faster than insert().</p>
<p>This article is originally posted <a href="https://community.dynamics.com/blogs/post/?postid=ce5a6a0e-e341-4834-863e-bf9ab518dbb5">here</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Import data through custom fields in LedgerJournalEntity]]></title><description><![CDATA[Recently, I came across a requirement to add custom fields in LedgerJournalTrans for importing Customer Expenses through General Journal.
So, I have created extension of LedgerJournalTrans and added n]]></description><link>https://bytebharani.hashnode.dev/import-data-through-custom-fields-in-ledgerjournalentity</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/import-data-through-custom-fields-in-ledgerjournalentity</guid><category><![CDATA[d365fo]]></category><category><![CDATA[D365 F&O]]></category><category><![CDATA[d365]]></category><category><![CDATA[#dynamics365]]></category><category><![CDATA[dynamics365fo]]></category><category><![CDATA[ledgerjournalentity]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Wed, 29 Apr 2026 16:06:28 GMT</pubDate><content:encoded><![CDATA[<p>Recently, I came across a requirement to add custom fields in LedgerJournalTrans for importing Customer Expenses through General Journal.</p>
<p>So, I have created extension of LedgerJournalTrans and added new fields and did the same in entity and staging table.</p>
<p>But out of my surprise, I cannot import the data through these new fields. I did model build, sync, regenerated mapping, refreshed entity. But nothing helped me. I can also see the data coming to staging but not to Entity or underlying tables. Then did the debug to see what actually is happening.</p>
<p><strong>What’s the outcome?</strong></p>
<p>On debug, I came to know that a method is written in this entity where actually the data from the staging is being copied to LedgerJournalEntity.</p>
<p>All the major functionality of this entity is found in this method, where I can see it copies the data from staging to target, validates the data etc. So first I tried doing COC on the methods which are found inside the present method which are responsible for insert and update of the data in LedgerJournalEntity. Only COC on update method worked but on create, COC did not work.</p>
<p>Then finally thought of doing a COC on complete method.</p>
<p>Then I did the post COC on copyCustomStagingToTarget() to achieve my requirement.</p>
<p>Below is the code which shows the COC on this method.</p>
<pre><code class="language-ruby">[ExtensionOf(tableStr(LedgerJournalEntity))]
final class MYLedgerJournalEntity_Extension
{
    public static container copyCustomStagingToTarget(DMFDefinitionGroupExecution _dmfDefinitionGroupExecution)
    {
        container ret;
        ret = next copyCustomStagingToTarget(_dmfDefinitionGroupExecution);
        LedgerJournalTrans ledgerJournalTrans;
        LedgerJournalEntityStaging staging;
        ledgerJournalTrans.skipDataMethods(true);

        update_recordset ledgerJournalTrans
                setting MYField1    =     staging.MYField1,
                        MYField2    =     staging.MYField2
            join staging
                    where staging.DefinitionGroup == _dmfDefinitionGroupExecution.DefinitionGroup
                    &amp;&amp; staging.ExecutionId == _dmfDefinitionGroupExecution.ExecutionId
                    &amp;&amp; staging.TransferStatus == DMFTransferStatus::Completed
                    &amp;&amp; staging.JournalBatchNumber == ledgerJournalTrans.JournalNum
                    &amp;&amp; staging.LineNumber == ledgerJournalTrans.LineNum;
        return ret;
    }

}
</code></pre>
<p>In this way, to populate the data into custom fields, COC on this method should be made.</p>
<p><strong>What’s the issue? Why I am not able to import?</strong></p>
<p>The reason why I am not able to import the data is, that the Set based processing (set based SQL operations) flag has been switched on for LedgerJournalEntity. So, data cannot be imported by normal process like how we do for other entites. So custom code is required for this.</p>
<p>This article is originally posted <a href="https://community.dynamics.com/blogs/post/?postid=1e54c2a2-5a90-449b-8dd6-c43ddc2f39ed">here</a>.</p>
<p>Happy Learning!</p>
]]></content:encoded></item><item><title><![CDATA[Recall/cancel and resubmit/submit a workflow through X++ code]]></title><description><![CDATA[Recently I came across a requirement to cancel and resubmit the workflow through code. Then I went through the form in UI and found the backend code in clicked method of the form. But I did not get th]]></description><link>https://bytebharani.hashnode.dev/recall-cancel-and-resubmit-submit-a-workflow-through-x-code</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/recall-cancel-and-resubmit-submit-a-workflow-through-x-code</guid><category><![CDATA[d365fo]]></category><category><![CDATA[d365]]></category><category><![CDATA[#dynamics365]]></category><category><![CDATA[dynamics365fo]]></category><category><![CDATA[workflow]]></category><category><![CDATA[workflow resubmit]]></category><category><![CDATA[submit workflow]]></category><category><![CDATA[cancel]]></category><category><![CDATA[recall]]></category><category><![CDATA[clean code]]></category><category><![CDATA[ERP]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Wed, 29 Apr 2026 16:00:22 GMT</pubDate><content:encoded><![CDATA[<p>Recently I came across a requirement to cancel and resubmit the workflow through code. Then I went through the form in UI and found the backend code in clicked method of the form. But I did not get the exact idea. Then out of curiosity, thought like I may have some class like Workflow. Fortunately, I found one.</p>
<p>Then I checked all the methods, used the find references and finally found 2 methods which are required for my purpose.</p>
<p>Now, lets check how can we do cancel or submit the workflow through code.</p>
<p><strong>Recall / Cancel a workflow</strong></p>
<p>To recall/cancel a workflow through code, simply call the static method called cancelWorkflow of Workflow class. The parameters, you need to provide here are the correlation id and a comment which we actually give when cancelling the workflow from UI.</p>
<p>The code looks like below.</p>
<pre><code class="language-ruby">Workflow::cancelWorkflow(workFlowTrackingTable.CorrelationId, "cancel by code");
</code></pre>
<p>Now the problem comes.</p>
<p>Where do I get the correlation Id?</p>
<p>It can be found in WorkflowTrackingStatusTable. To get the exact buffer, you must give a select on this table with where condition on InstanceNumber from this table.</p>
<p>So, the final code would be as below.</p>
<pre><code class="language-ruby">WorkflowTrackingStatusTable                 workFlowTrackingTable;

select firstonly CorrelationId from workFlowTrackingTable where workFlowTrackingTable.InstanceNumber == '002647';

Workflow::cancelWorkflow(workFlowTrackingTable.CorrelationId, "cancel by code");
</code></pre>
<p>So first we should find the exact record of that workflow to be submitted, then call the cancelWorkflow method.</p>
<p><strong>Resubmit / Submit a workflow</strong></p>
<p>To resubmit or submit a workflow, you need to call again another static method from same class called “activateFromWorkflowType”.</p>
<p>This can be done as below.</p>
<pre><code class="language-ruby">Workflow::activateFromWorkflowType(workflowtypestr(HcmDiscussion), hcmDiscussion.RecId, "SubmittedBy by Code", NoYes::No);
</code></pre>
<p>But wait.</p>
<p>We also have a return type here called WorkflowCorrelationId in the above static method. So, the code will be,</p>
<pre><code class="language-ruby">WorkflowCorrelationId		workflowCorrelationId;
HcmDiscussion               hcmDiscussion;

hcmDiscussion = HcmDiscussion::find(5637146584);

workflowCorrelationId = Workflow::activateFromWorkflowType(workflowtypestr(HcmDiscussion), hcmDiscussion.RecId, "SubmittedBy by Code", NoYes::No);
</code></pre>
<p>As my workflow is in HRM module, I am using the table called HcmDiscussion. Based on the place where we will work with the workflow, we should get the table name accordingly and we need to pass the parameters accordingly. The logic to submit workflow will be same across all modules or tables but based on the place, the table should be selected.</p>
<p>Coming to the method activateFromWorkflowType, the parameters to be passed are explained below.</p>
<p>The first one is the workflowtype for that module (if you are not able to get it you can make a find reference and find what is being using in that module), then the RecId of the table involved, comment, if activating from web yes otherwise no. We also have another parameter which has default value like curUserid(). We can also change the last parameter on requirement.</p>
<p>In this way we can simply cancel or submit the workflow through code. This logic can be implemented for multiple records or in batch or with different tables too.</p>
<p>This article is originally posted <a href="https://community.dynamics.com/blogs/post/?postid=63e88136-59b6-484e-8701-3addd17b1c26">here</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Ternary Operator instead of if – else statement in X++]]></title><description><![CDATA[I mainly emphasize on writing the required code. So that I will cut/remove the code whichever is unnecessary. So, when I came across if – else condition, thought of having a single line of code. Then ]]></description><link>https://bytebharani.hashnode.dev/d365fo-ternary-operator-tips</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/d365fo-ternary-operator-tips</guid><category><![CDATA[ternary operator]]></category><category><![CDATA[d365fo]]></category><category><![CDATA[d365]]></category><category><![CDATA[d365 finance and operations ,]]></category><category><![CDATA[dynamics365fo]]></category><category><![CDATA[clean code]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Wed, 29 Apr 2026 15:48:42 GMT</pubDate><content:encoded><![CDATA[<p>I mainly emphasize on writing the required code. So that I will cut/remove the code whichever is unnecessary. So, when I came across if – else condition, thought of having a single line of code. Then I found this Ternary operator in X .</p>
<p>Ternary operation statement is used for executing conditional code. It can be directly assigned to a variable. It is also possible to do same thing using if – else. But for if - else, it will look like a code flow which increases the code size which is not necessary.</p>
<p>For example, an if – else statement looks like,</p>
<pre><code class="language-ruby">int i = 10;
str output;

if (i &gt; 2)
    {
      output = “true”;
    }
else
    {
     output = “false”;
    }
    
info(strfmt(“%1”,output)); //returns true
</code></pre>
<p>Here we have a variable of value 10 and if it is greater than 2 then output will be true which will appear as Infolog.</p>
<p>The same code can be written in little less steps and can directly assign to a variable using Ternary Operation.</p>
<pre><code class="language-ruby">int i = 10;
str output = i &gt; 2 ? “true” : “false”;
info(strfmt(“%1”, output)); //returns true
</code></pre>
<p>Here I am directly assigning the value i.e., true or false to the output based on i value.</p>
<p>Like in the ternary operation, after equal to, the first expression is to evaluate the condition. Based on the condition, if it is true then the 2nd expression or value will be assigned to the variable or the 3rd expression or value will be assigned. So, we will get true as a Infolog since i is greater than 2.</p>
<p><strong>Nested Ternary Operator</strong></p>
<p>We sometimes need to write nested Ternary operation.</p>
<p>The below example is taken from standard Microsoft Documentation.</p>
<pre><code class="language-ruby">print( (custTable.AccountNum &gt; "1000")
            ? ( (custTable.AccountNum &lt; "2000")
                    ? "In interval" : "Above 2000"
              )
            : "low"
     );
</code></pre>
<p>The code processing logic will be if the customer account number is greater than 1000, then it goes into loop and checks if it is less than 2000. If it is less than 2000, then it prints “In interval” or if it is greater, then it prints “Above 2000”. If the customer number is less than 1000 then it prints low.</p>
<p>Now you must have understood the code flow.</p>
<p>By this, the code actually looks neat and clean with less steps.</p>
<p>So, with simple steps we can complete an if – else using ternary operation. So, it would be better to give the ternary the first preference and then if-else if we have a similar requirement.</p>
<p>The syntax is,</p>
<p><strong>expression1 ? expression2 : expression3</strong></p>
<p>and the explanation is, expression1 must be a Boolean expression. If expression1 is true, the whole ternary statement resolves to expression2. Otherwise, it resolves to expression3. expression2 and expression3 must be of the same type as each other.</p>
<p>This article is originally posted <a href="https://community.dynamics.com/blogs/post/?postid=08b1d38c-c6ee-4826-b041-d13299554425">here</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Find method and its usage]]></title><description><![CDATA[First, we will understand what this find method is and why is this important in D365 FO.
For storing all the data in D365, we have a database where all data will be stored as records in backend tables]]></description><link>https://bytebharani.hashnode.dev/find-method-and-its-usage</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/find-method-and-its-usage</guid><category><![CDATA[.findmethod]]></category><category><![CDATA[d365fo]]></category><category><![CDATA[d365]]></category><category><![CDATA[coding]]></category><category><![CDATA[FIND]]></category><category><![CDATA[D365 F&O]]></category><category><![CDATA[d365 finance and operations ,]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Wed, 29 Apr 2026 14:40:05 GMT</pubDate><content:encoded><![CDATA[<p>First, we will understand what this find method is and why is this important in D365 FO.</p>
<p>For storing all the data in D365, we have a database where all data will be stored as records in backend tables. Most of the times we need to pick this data, edit or do something with this data in backend. We may create reports also. So, such times we will use select statement or a find method to retrieve the data.</p>
<p>So, what this find method is?</p>
<p>The find method is a static method at table which is used instead of a select statement in X coding. For example, to select a single record from VendTable, we will give the following select statement.</p>
<pre><code class="language-swift">VendTable     vendTable;

select firstonly AccountNum from vendTable where vendTable.AccountNum == “Vend1”;
info(strFmt(“%1”, vendTable.AccountNum));
</code></pre>
<p>'firstonly' keyword as we have a single record for that particular ID and selecting only AccountNum as we require only that field to show as a info statement.</p>
<p>So, the above select statement returns that record where AccountNum is “Vend1”. </p>
<p>This can also be done by using below find method.</p>
<pre><code class="language-swift">VendTable  vendTable = VendTable::find(“Vend1”);
info(strFmt(“%1”, vendTable.AccountNum));
</code></pre>
<p>This will also return the same thing. We can use any of them.</p>
<p>But if we require just few fields then I would recommend using normal select statement explicitly mentioning each and every field. Because the find method will retrieve data from all the fields. If all the fields’ data is not required, then it is not necessary to use find method at such cases.</p>
<p>Now how to write the find method in any custom table?</p>
<p>You can simply copy any standard table find method and use that in your code as below.</p>
<p>Below is the find method for VendTable.</p>
<pre><code class="language-swift">public static VendTable find(
        VendAccount             _vendAccount ,
        boolean                 _forupdate        = false,
        ConcurrencyModel        _concurrencyModel = ConcurrencyModel::Auto)
    {
        VendTable vendTable;

        vendTable.selectForUpdate(_forupdate);
        if (_forupdate  &amp;&amp; _concurrencyModel != ConcurrencyModel::Auto)
        {
            vendTable.concurrencyModel(_concurrencyModel);
        }

        if (_vendAccount != '')
        {
            select firstonly vendTable
                where vendTable.AccountNum == _vendAccount;
        }

        return vendTable;
    }
</code></pre>
<p>This method can be copied into your table method and should be changed accordingly like in the name of VendTable, your custom table name and in the place of VendAccount, your index field name. </p>
<p>Now the use of find method is, as we have 2 parameters where 2nd parameter is optional or default,</p>
<ul>
<li><p>If you just give the Vendor account number, then it will retrieve the only record with that vendor account number taking into consideration the other parameter _forupdate is false. </p>
</li>
<li><p>If you give both vendor number and _forupdate to true, then it will retrieve the record and update the record as well.</p>
</li>
<li><p>The unique index can be found simply by looking at the find method as the mandatory parameters in find method are unique.</p>
</li>
</ul>
<p>By this we can understand that a find method can be used to retrieve the data or update the data. We can also use this for finding the unique index of the table.</p>
<p>This is also best practice to include find method in the table.</p>
<p>This is originally posted <a href="https://community.dynamics.com/blogs/post/?postid=dca22239-8a49-41c0-bd59-f5e26d998210">here</a>.</p>
]]></content:encoded></item><item><title><![CDATA[How to post packing slip or invoice for a particular line in a Sales Order using X++]]></title><description><![CDATA[Requirement:
We have a requirement to post only specified sales lines in a Sales Order from X++. To address this requirement, I did my own analysis and achieved this with the below solution.
So lets c]]></description><link>https://bytebharani.hashnode.dev/how-to-post-packing-slip-or-invoice-for-a-particular-line-in-a-sales-order-using-x</link><guid isPermaLink="true">https://bytebharani.hashnode.dev/how-to-post-packing-slip-or-invoice-for-a-particular-line-in-a-sales-order-using-x</guid><category><![CDATA[d365fo]]></category><category><![CDATA[packing slip]]></category><category><![CDATA[salesorderlinewise]]></category><category><![CDATA[X++]]></category><category><![CDATA[sales invoice]]></category><dc:creator><![CDATA[Bharani Preetham Peraka]]></dc:creator><pubDate>Sun, 19 Apr 2026 17:06:19 GMT</pubDate><content:encoded><![CDATA[<h3>Requirement:</h3>
<p>We have a requirement to post only specified sales lines in a Sales Order from X++. To address this requirement, I did my own analysis and achieved this with the below solution.</p>
<p>So lets check what is the solution for this..:)</p>
<h3>Solution:</h3>
<p>We can leverage the existing SalesFormLetter class for this requirement. This class is commonly used for posting Sales Orders, and it provides a convenient chooseLinesQuery() method to select specific lines. Simply passing a standard SalesUpdate query with the appropriate filters like the Sales Id and the lines numbers to this method helps us in posting specific lines.</p>
<h3>Explanation:</h3>
<p>Here I have created a query object for SalesUpdate query and collected the lines into a container to pass them as a range along with the Sales ID. This combined information is then used to construct a query. Once the query is built, a queryRun object is created and fed it into the chooseLinesQuery() method of the SalesFormLetter class. This method instructs the SalesFormLetter to post only the lines included in the provided range.</p>
<p>The below code does the Invoice Posting. Same thing can be used for Packing Slip as well.</p>
<pre><code class="language-csharp">SalesFormLetter salesFormLetter = SalesFormLetter::construct(DocumentStatus::Invoice);

if (this.parmLineNums() != conNull())
{
       Query query = new Query(queryStr(SalesUpdate));
           query.dataSourceTable(tableNum(SalesLine)).addRange(fieldNum(SalesLine, SalesId)).value(salesTable.SalesId);
       query.dataSourceTable(tableNum(SalesLine)).addRange(fieldNum(SalesLine, LineNum)).value(con2Str(this.parmLineNums()));

       SysQueryRun queryRun = new SysQueryRun(query);
       salesFormLetter.chooseLinesQuery(queryRun);
}

salesFormLetter.update(salesTable, DateTimeUtil::getSystemDate(DateTimeUtil::getUserPreferredTimeZone()), SalesUpdate::All, AccountOrder::None, NoYes::No, NoYes::No, NoYes::No, NoYes::Yes);
</code></pre>
<p>Using the chooseLinesQuery() method this scenario can be achieved.</p>
<p>PS:- To stick with the discussion, I explained or added the code only w.r.t to posting of specific line. Table buffers like SalesTable which I am using doesn't have initialization here since complete code has not been added.</p>
]]></content:encoded></item></channel></rss>