Tag Archives: Finance and Operations

Pass the parameter from D365 FO to Power BI Embedded and Filter records in Power BI

Requirement – User will open power bi embedded in Microsoft Dynamics 365 for finance and operations. Pre-filtered data of vendor purchase invoices should come only for those warehouses which are tagged to current user or running user id.

Solution – In D365 FO we use class- PBIReportControllerBase to call a Power BI embedded report. The class PBIReportControllerBase contains below methods. The method basicFilters() can be utilized to filter your Power BI datasets with the help of class PBIReportBasicFilter

In standard there are 2 classes – FMPBIReportsController (Model – Fleet Management) & LedgerCFOWorkspaceEmbeddedController. Here, Microsoft is filtering power BI Record set based on selected values (Please make a note – table name and column name should be same as Power BI Report dataset and column name Not your aggregate measure or AX DW tables and column)

So, in our case we are filtering dataset based on warehouse tagged with current users

Cheers,

Piyush Adhikari

Combine & Merging images to a single image using X++ & C# Dot Net Libraries

How to Merge Two Brands: Successful M&A

Requirement – The requirement is to combine and merging images to a single image using X++ & C# Dot Net Libraries in Microsoft Dynamics 365 finance and operation.

Classes & Libraries –

System.Drawing.Bitmap
System.Drawing.Image
System.Drawing.Graphics
System.Drawing.Imaging.ImageFormat
System.Drawing.Color

Sample Code –

str jpg1 = @”c:\images.jpeg”;
str jpg2 = @”c:\images2.jpeg”;
str jpg3 = @”c:\image3.jpg”;

System.Drawing.Image img1 = System.Drawing.Image::FromFile(jpg1);
System.Drawing.Image img2 = System.Drawing.Image::FromFile(jpg2);

int width = img1.Width + img2.Width;
int height = System.Math::Max(img1.Height, img2.Height);

System.Drawing.Bitmap img3 = new System.Drawing.Bitmap(width, height);

System.Drawing.Graphics g = System.Drawing.Graphics::FromImage(img3);

g.Clear(System.Drawing.Color::Black);
g.DrawImage(img1, new Point(0, 0));
g.DrawImage(img2, new Point(img1.Width, 0));

g.Dispose();
img1.Dispose();
img2.Dispose();

img3.Save(jpg3, System.Drawing.Imaging.ImageFormat::Jpeg);
img3.Dispose();

D365 FO : Developing Business Events with X++ from scratch

Microsoft Dynamics AX/D365 F&O Technical blog

Business events provide a mechanism that allow external system receives notifications from Microsoft Dynamics 365 Finance and operations. For example – When you are posting a purchase invoice then you can send notifications/ payload / message to an external system.

You can use existing standard business events which are available in the D365 FO or you can also develop and customize new business events as per your need.

There are 2 ways you can consume business events . 1. Power automate (Microsoft Flow) 2. Azure messaging services.

Scenario – In this example, I am going to show you how we can develop a new custom business event from scratch. In my scenario, business event in D365 FO should be triggered when a user is posting purchase invoice in Microsoft dynamics 365 for finance and operations.

There a 2 standard and base classes which i will use to develop custom business events –

  1. BusinessEventsContract ( Use for developing/defining payload message)
  2. BusinessEventsBase ( Use for triggering custom business event)

Create a PAAPurchInvoiceJournalPostBusinessEventContract class

[DataContract]
class PAAPurchInvoiceJournalPostBusinessEventContract extends BusinessEventsContract
{
private VendAccount vendAccount;
private PurchId purchid;
private InvoiceId invoiceId;

private void initialize(VendInvoiceJour _vendInvoiceJour)
{
vendAccount = _vendInvoiceJour.OrderAccount;
purchid = _vendInvoiceJour.PurchId;
invoiceId = _vendInvoiceJour.InvoiceId;
}

public static PAAPurchInvoiceJournalPostBusinessEventContract newFromVendInvoiceJour(VendInvoiceJour _vendInvoiceJour)
{
PAAPurchInvoiceJournalPostBusinessEventContract contract = new PAAPurchInvoiceJournalPostBusinessEventContract();
contract.initialize(_vendInvoiceJour);
return contract;
}

private void new()
{
}

[DataMember(‘VendAccount’), BusinessEventsDataMember(‘VendAccount’)]
public vendAccount parmvendAccount(vendAccount _vendAccount = vendAccount)
{
vendAccount = _vendAccount;
return vendAccount;
}

[DataMember(‘PurchId’), BusinessEventsDataMember(“PurchId”)]
public PurchId parmPurchId(PurchId _purchId = purchId)
{
purchId = _purchId;
return purchId;
}

[DataMember(‘InvoiceId’), BusinessEventsDataMember(“InvoiceId”)]
public InvoiceId parmInvoiceId(InvoiceId _invoiceId = invoiceId)
{
invoiceId = _invoiceId;
return invoiceId;
}

}

Create a PAAPurchInvoiceJournalPostBusinessEvent class

[BusinessEvents(classStr(PAAPurchInvoiceJournalPostBusinessEventContract),
“Custom Vendor Invoice Post Business Event”,
“This business event is triggering during the time purchase invoice posting”,ModuleAxapta::AccountsPayable)]
class PAAPurchInvoiceJournalPostBusinessEvent extends BusinessEventsBase
{
private VendInvoiceJour vendInvoiceJour;

static public PAAPurchInvoiceJournalPostBusinessEvent newFromVendInvoiceJour(VendInvoiceJour _vendInvoiceJour)
{
PAAPurchInvoiceJournalPostBusinessEvent businessEvent = new PAAPurchInvoiceJournalPostBusinessEvent();

businessEvent.parmVendInvoiceJour(_vendInvoiceJour);
return businessEvent;
}

private VendInvoiceJour parmVendInvoiceJour(VendInvoiceJour _vendInvoiceJour = vendInvoiceJour)
{
vendInvoiceJour = _vendInvoiceJour;
return vendInvoiceJour;
}

private void new()
{
}

[Wrappable(true), Replaceable(true)]
public BusinessEventsContract buildContract()
{
return PAAPurchInvoiceJournalPostBusinessEventContract::newFromVendInvoiceJour(vendInvoiceJour);
}

}

you must add below block of code in purchase invoice posting routine class. Also business must be activated or enabled in that company. Business event will trigger when purchase invoice posting will take place.

VendInvoiceJour vendInvoiceJour = this; // vendInvoiceJour buffer

if(BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(PAAPurchInvoiceJournalPostBusinessEvent)))
{
PAAPurchInvoiceJournalPostBusinessEvent::newFromVendInvoiceJour(this).send () ;

}

Build your project solution and navigate to path -[System administration–> Setup –> Business Events –> Business Events Catalog] to see and activate your business event.

Archive inventory transactions (Purge InventTrans) in Microsoft Dynamics 365 for finance and operations

As we know, “InventTrans” table is one of the largest table in Microsoft D365 for finance and operations and this table keep growing through out the ERP lifecycle and consume more space. BUT ! here is the good news that Microsoft has released a standard feature and a standard batch job to archive inventory transactions and purge InventTrans table data.

Here is the Microsoft document link – https://docs.microsoft.com/en-us/dynamics365/supply-chain/inventory/archive-inventory-transactions

cheers,

Piyush Adhikari

D365 FO – Create deep links, shareable, secured URLs in Microsoft Dynamics 365 for finance & operations

A complete guide to mobile app deep linking | Adjust

What is deep links in Microsoft Dynamics 365 for finance and operation? Deep links are shareable and secures URLs to a specific form. Optional filters can be passed so that when user will open the deep link and navigate to a specific form then it will show filtered data ore records. In Microsoft D365 FO, below library – Microsoft.Dynamics.AX.Framework.Utilities.UrlHelper.UrlGenerator is used to generate deep links using X++ .

Sample Code – Below is the sample code of developing & generating deep links for D365 FO forms and records using filter and ranges in form data source. In below example, i am generating the sharable URL (deep link ) of a form – CustAccount and applying a “CustAccount” filter on custTable datasource.

using Microsoft.Dynamics.AX.Framework.Utilities; // setting library/Dll reference

class DeepLinkTest
{
    public static void main(Args _args)
    {
        MenuItemNameDisplay menuItemName                = menuItemDisplayStr(CustTable);  // customer form
        MenuItemType        menuItemtype                = MenuItemType::Display;
        str                 filterformDataSource        = formDataSourceStr(CustTable, CustTable);  // filer datasource
        str                 filterFormDataSourceField   = fieldStr(CustTable, AccountNum); // filter field

        UrlHelper.UrlGenerator generator = new UrlHelper.UrlGenerator();
        System.Uri currentHost = new System.Uri(UrlUtility::getUrl());
 
        generator.HostUrl = currentHost.GetLeftPart(System.UriPartial::Authority);
        generator.Company = curExt();
        generator.MenuItemName = menuItemName;
        generator.MenuItemType = menuItemtype;
        generator.Partition = getCurrentPartition();
        generator.EncryptRequestQuery = true; 
        if(filterformDataSource)
        {
            UrlHelper.RequestQueryParameterCollection requestQueryParameterCollection;
 
            requestQueryParameterCollection = generator.RequestQueryParameterCollection;
            requestQueryParameterCollection.UpdateOrAddEntry(filterformDataSource, filterFormDataSourceField, "CustAccount"); // applying filter
        } 

        System.Uri fullURI = generator.GenerateFullUrl();
 
        Info(fullURI.AbsoluteUri); // getting deep link
    }

}

D365 FO/AX7: Cross-company & Change-company for accessing Data for Multiple Companies Using X++

Introduction

In Microsoft Dynamics AX/D365 Finance and operation, users can setup multiple legal entities. By default in Microsoft Dynamics AX/D365 FO, all tables store data per company unless SaveDataPerCompany  property is not changed to NO. By dafault value of property SaveDataPerCompany  = Yes.Using crosscompany & changecompany keyword, you can construct a query to retrieve all records, regardless of the company you are currently logged into.You can fetch the all the company records using keyword -crosscompany and function – changecompany.

Change-Company

static void main()
{
  CustTable custTable;
  changeCompany('INMF') 
  {
    custTable = null;
    while select custTable
    {
       //custTable of company 'INMF'.
    }
  }
}

Cross-Company

static void main()
{
  CustTable custTable;
  while select crosscompany custTable
  {
      custTable of all the companies
  }
}

AX7/D365: JSON DeSerialization/Parsing Using FormJsonSerializer

Requirement:

Developer is consuming an external REST web-service in D365/X++ . Response is in the JSON format.  Developer needs to DeSerialize incoming JSON so that values can be used later in the system.

Sample Code:

  1. Create a contract class
    [DataContract]
    class JSONContract
    {
        String50 parmFirst, parmSecond;
        [
            DataMemberAttribute('parmFirst')
        ]
        public String50 parmFirst(String50 _parmFirst = parmFirst)
        {
            parmFirst = _parmFirst;
            return parmFirst;
        }
        [
            DataMemberAttribute('parmSecond')
        ]
        public String50 parmSecond(String50 _parmSecond = parmSecond)
        {
            parmSecond = _parmSecond;
            return parmSecond;
        }
    }
  2. Sample Class
    class RunnableClassABC
    {
        /// <summary>
        /// Runs the class with the specified arguments.
        /// </summary>
        /// <param name = "_args">The specified arguments.</param>
        public static void main(Args _args)
        {
            str             json;
            List            values = new List(Types::String);
            ListEnumerator  value;      
            json = '[{"FieldValues": [{"parmFirst": "ValueFirst", "parmSecond": "ValueSecond", "parmList":[{"parmFirst": "ValueFirst", "parmSecond": "ValueSecond"}] }]}]';
            // Deserializing Json
            values = FormJsonSerializer::deserializeCollection(classnum(List), json, Types::Class, classStr(JSONContract));
            value = values.getEnumerator();
            while(value.moveNext())
            {
                JSONContract    JSONContractCurrent = value.current();
                info(strFmt("%1    %2",JSONContractCurrent.parmFirst(), JSONContractCurrent.parmSecond()));
            }
        }
    }

D365/AX7: Create Inventory Movement Journal Using X++ Code

public static void main(Args _args)
{ 
InventTable inventTable;
InventSite inventSite;
InventJournalTrans inventJournalTrans;
InventDim inventDimFrom;
InventJournalTable inventJournalTable;
int lineNum;
InventParameters inventParameters = InventParameters::find();
inventJournalTable.clear();
inventJournalTable.initValue();
inventJournalTable.initFromInventJournalName(InventJournalName::find(inventParameters.MovementJournalNameId));
if (inventJournalTable.validateField(fieldNum(InventJournalTable, JournalNameId)))
{
inventJournalTable.modifiedField(fieldNum(InventJournalTable, JournalNameId));
inventJournalTable.JournalType = InventJournalType::Movement;
inventJournalTable.insert();
}

lineNum ++;
inventJournalTrans.clear();
inventJournalTrans.initValue();
inventJournalTrans.initFromInventJournalTable(inventJournalTable);
inventJournalTrans.TransDate = today();
inventJournalTrans.ItemId = '1104X';
inventJournalTrans.LineNum = lineNum;
inventJournalTrans.modifiedField(fieldNum(InventJournalTrans, ItemId));
inventDimFrom.clear();
inventDimFrom.initValue();
inventDimFrom.InventSiteId = 'Site 1';
inventDimFrom.InventLocationId = '11';
inventDimFrom = InventDim::findOrCreate(inventDimFrom);
inventJournalTrans.InventDimId = inventDimFrom.inventDimId;
inventJournalTrans.modifiedField(fieldNum(InventJournalTrans, inventDimId));
inventJournalTrans.Qty = 2;
inventJournalTrans.modifiedField(fieldNum(inventJournalTrans, Qty));
inventTable = InventTable::find(inventJournalTrans.ItemId);
inventJournalTrans.DefaultDimension = LedgerDimensionDefaultFacade::serviceMergeDefaultDimensions(inventTable.DefaultDimension, inventDimFrom.inventSite().DefaultDimension);
inventJournalTrans.JournalType = InventJournalType::Movement;
inventJournalTrans.insert();
}