Convert Power Apps Portal Into A Progressive Web Apps (PWA) : Introduction & Demo

What is Progressive Web App (PWA) ?

PWA is a web app that is optimized to run “like an app” on a mobile device .They look & feel like a Native App and also appear to be a like a native app that you would install from your device’s app store, but actually work within a standard browser. From a development perspective, if you know how to create a web application using HTML, CSS and JavaScript (and now Power Apps portals) you can use these same building blocks to build a mobile application. Twitter, Uber, Pinterest, Tinder, and Google Maps are just some examples of large companies that have embraced PWAs.

What are the benefits of Progressive Web App ?

  • Fast loading and works offline
  • Can be launched from home screen and receive the push notification
  • Junk free scrolling, Smooth Animations, and seamless navigation even on poor networks
  • No app store needed. Users can discover and download apps right from the web, making PWAs more accessible.
  • Run on all devices: website, web app, mobile, desktop

Power Apps Portal as a Progressive Web App (PWA)

You can enable progressive web app (PWA) in Microsoft Power apps portal.

  • You can also customize PWA and can also change offline behaviour.A PWA can be configured to continue to run on a mobile device when connectivity is lost or put in an offline mod
  • Once your PWA customization will be completed, you can create or generate an app package and distribute in Google Play store or Microsoft Store. To create app package, select Create app package under App package in Progressive web app in portals Studio. You’ll be taken to PWA builder where you can create app package for various app stores such as Microsoft Windows Store and Google Play Android Store and step-by-step document to publish app in respective store. You can now publish app in respective app store console.

Resource –

https://powerapps.microsoft.com/en-us/blog/power-apps-portals-as-mobile-apps-preview/

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.

What is AdminUserProvisioning Tool in Microsoft D365 fin & ops ? Add Admin user after backup restore

In various ERP implementation & support projects, sometime it is very hard to replicate a production / UAT environment scenario in dev box for debugging or in some cases it is tedious to do any kind of unit testing due to lack of business data. But luckily, we can solve this problem by doing restoration of transaction database backup in your dev box from a UAT or production .In most of cases, developer can only access in his/her development box and developer is not allowed to access in UAT/Production sever . After restoring database backup, developer starts to face issue – “You are not authorized to login with your current credentials. You will be redirected to login page in few seconds” because his/her developer login ID is not available in AXDB D365 FO database .

To solve this issue, Microsoft has provided a tool called – AdminUserProvisioning.exe . After restoring the database from your UAT/PROD server in your development box, you can add your userId in your dev box D365 FO instance.

The path of tool AdminUserProvisioning.exe is C:\AOSService\PackagesLocalDirectory\bin\AdminUserProvisioning.exe

OR

K:\AOSService\PackagesLocalDirectory\bin\AdminUserProvisioning.exe

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 Fin & Ops : ZIP, Compress & Archive multiple files using X++ & class ZipArchiveEntry

Requirement : The requirement is to create a zip file from a set of document entries available in document handling, creating a download link and sends the link to user browser.

In this requirement we are using Microsoft Dynamics 365 for finance and operations classes – documentmanagement, ziparchive & ziparchiveentry.

Sample Code :

class RunnableClassZIP
{
    /// <summary>
    /// Runs the class with the specified arguments.
    /// </summary>
    /// <param name = "_args">The specified arguments.</param>
    public static void main(Args _args)
    {
        System.IO.Stream fileStream;
        System.IO.MemoryStream zipArchiveStream = new System.IO.MemoryStream();
        DocuRef docuRef;
        DocuValue docuValue;
        
        const str extensionZip = '.zip';
        const str zipFileName = 'myZipfile';
    
        using (System.IO.Compression.ZipArchive zipArchive = new System.IO.Compression.ZipArchive(
            zipArchiveStream,
            System.IO.Compression.ZipArchiveMode::Create,
            true))
        {
            while select docuRef
                join docuValue
            where docuValue.RecId   == docuRef.ValueRecId
            && docuRef.RefTableId   == 'your table id'
            && docuRef.RefRecId     == 'your rec id'
            {            
                // Creates the file entry in the zip folder.
                System.IO.Compression.ZipArchiveEntry fileEntry = zipArchive.CreateEntry(docuValue.filename());
            
                // Opens the created file entry and copies the binary file information of the original file to the file entry in the zip folder.
                using (System.IO.Stream fileEntryStream = fileEntry.Open())
                {
                    // Gets the file stream of the document attachment.
                    fileStream = DocumentManagement::getAttachmentStream(docuRef);
                    fileStream.CopyTo(fileEntryStream);
                }
            }

            // Send a download link of the created zip folder to the user.
            File::SendFileToUser(zipArchiveStream, zipFileName + extensionZip);
        }
    }

}

Microsoft Dynamics AX, Microsoft Dynamics 365 for Finance and Operations, D365 FO, Retail, SQL, Microsoft Power Apps, Microsoft Power BI, Microsoft Azure, Logic Apps, Microsoft Flow, Microsoft power automate, Microsoft Power Platform,.Net, X++, C#, Power BI DAX, Data Warehousing, Microsoft Analysis Services, SQL Server Reporting Services