Skip to main content

Electronic reporting (ER) - Create custom destinations

Electronic reporting (ER) is a configurable tool in Microsoft Dynamics 365 that helps create and maintain regulatory electronic reporting and payments. Configuration of reports can be done by business users with the use of Visual Editors without the need for developers.

Refer to Electronic reporting (ER) overview - Finance & Operations | Dynamics 365 | Microsoft Docs for more information and detailed overview on ER.

Destinations can be configured for each ER format configuration and its output component. Destinations can only be set up for ER configurations that have been imported into the current Finance instance, and for the formats that are available on the Electronic reporting configurations page. The functionality for ER destination management is available at Organization administration > Electronic reporting > Electronic reporting destination. 

Microsoft Dynamics 365 for Operations version 1611 (November 2016) or later allows the use of  the following destination types:

• Email

• Archive

• File

• Screen

• Power BI

You may refer to Electronic reporting (ER) destinations - Finance & Operations | Dynamics 365 | Microsoft Docs for more information on ER destinations.

We will look at adding additional destinations for electronic reports to be sent to. In this example we will look at adding a custom destination that allows electronic reports to be saved to a specific folder on the local system. We will call this destination as "Save to folder". The user will be able to enable/disable the "Save to folder" destination, and be able to specify the folder path to where the electronic file report needs to be saved on the local file system.

Custom Destionation for ER


Firstly, extend the 'ERFormatDestinationSettings' form and add a tab called "SaveToFolder". Add a checkbox, and String input field to the "SaveToFolder" tab.



Next, add a new class called 'ERFileDestinationSaveToFolder' that will make use of the existing public Electronic reporting file destination interface to implement the "SaveToFolder" custom destination.

using Microsoft.Dynamics365.LocalizationFramework;
public class ERFileDestinationSaveToFolder implements ERIFileDestination
{
    private str folderPath;
    public str parmFolderPath(str _value = folderPath)
    {
        folderPath = _value;
        return folderPath;
    }
    [Hookable(false)]
    public System.IO.Stream saveFile(System.IO.Stream _stream, str _filePath)
    {
        this.sendFileToFolder(_stream, System.IO.Path::GetFileName(_filePath));
        return _stream;
    }
    [Hookable(false)]
    public System.IO.Stream newFileStream(str _filePath)
    {
        return new System.IO.MemoryStream();
    }
    private str getFileName(str _fileName)
    {
        System.DateTime now = DateTimeUtil::utcNow();
        return strFmt('%1_%2%3',
            System.IO.Path::GetFileNameWithoutExtension(_fileName),
            now.ToString('yyyy-M-d_HH_mm_ss'),
            System.IO.Path::GetExtension(_fileName)
        );
    }
    private void sendFileToFolder(System.IO.Stream _stream, str _fileName)
    {
        var resultPath = System.IO.Path::Combine(this.folderPath,
                            this.getFileName(_fileName));
        using(var fileStream = System.IO.File::Create(resultPath))
        {
            if( _stream.CanSeek)
            {
                _stream.Seek(0, System.IO.SeekOrigin::Begin);
            }
            _stream.CopyTo(fileStream);
        }
    }
    /// <summary>
    /// Finalizes files processing.
    /// </summary>
    [Hookable(false)]
    public void finalize()
    {
    }
}

Now, create a 'ERFormatDestinationSaveToFolderSettings' class that will create the destination of the electronic report based on the newly added custom destination, and create the presentation of the "SaveToFolder" destination on the User interface, as well as populate the values of the enable/disable checkbox and filepath. This class will also pack/unpack the parameters for storage and be used to update the values of the custom destination whenever the user makes a change on the UI.

using Microsoft.Dynamics365.LocalizationFramework;
public class ERFormatDestinationSaveToFolderSettings implements
    ERIFormatFileDestinationSettings
{
    internal const str SettingsKey =
    classStr(ERFormatDestinationSaveToFolderSettings);

    public const int CurrentVersion = 1;
    private boolean isEnabled;
    private str folderPath;
    [Hookable(false)]
    public str parmFolderPath(str _value = folderPath)
    {
        folderPath = _value;
        return folderPath;
    }
    [Hookable(false)]
    public boolean parmIsEnabled(boolean _value = isEnabled)
    {
        isEnabled = _value;
        return isEnabled;
    }
    [Hookable(false)]
    public void setEnabled(boolean _value)
    {
        isEnabled = _value;
    }
    [Hookable(false)]
    public boolean isEnabled()
    {
        return isEnabled;
    }
    [Hookable(false)]
    public str getName()
    {
        return 'Save to folder';
    }
    [Hookable(false)]
    public void validate()
    {
    }
    [Hookable(false)]
    public ERIFileDestination createDestination(
        ERDestinationExecutionContext _destinationContext,
        boolean _isGrouped)
    {
        ERFileDestinationSaveToFolder ret = new ERFileDestinationSaveToFolder();
        ret.parmFolderPath(this.parmFolderPath());
        return ret;
    }
    [Hookable(false)]
    public str getKey()
    {
        return SettingsKey;
    }
    [Hookable(false)]
    public container pack()
    {
        return [CurrentVersion, isEnabled, folderPath];
    }
    [Hookable(false)]
    public boolean unpack(container packedClass)
    {
        int version = RunBase::getVersion(packedClass);
        switch (version)
        {
            case CurrentVersion:
                [version, isEnabled, folderPath] = packedClass;
                return true;
            default:
                return false;
        }
    }
    /// <summary>
    /// Creates a new instance of an objected from a packed class container.
    /// </summary>
    /// <param name = "_packedClass">A packed class container.</param>
    /// <returns>The new instance of an object.</returns>
    public static ERFormatDestinationSaveToFolderSettings create(
        container _packedClass)
    {
        var ret = new ERFormatDestinationSaveToFolderSettings();
        ret.unpack(_packedClass);
        return ret;
    }
}

Finally, create an extension class of the form that extends the 'ERFormatDestinationSettings '  form method. We will create CoC extensions for the init() and closeOk() methods. This is where the logic for the enable/disable checkbox and folder path string will be placed.

[ExtensionOf(formstr(ERFormatDestinationSettings))]
final class ERFormatDestinationSettings_FormMethodStr_Extension
{
    public void init()
    {
        next init();
        Object formRunObj = any2Object(this) as Object;
        if (formRunObj is ERIFormatFileDestinationSettingsStorage)
        {
            FormRun formRun = formRunObj;
            ERIFormatFileDestinationSettingsStorage settingsStorage = formRunObj ;
            var settings = this.getSaveToFolderSettings(settingsStorage);
           
            FormCheckBoxControl enabledControl = formRun.design().controlName(
                formControlStr(ERFormatDestinationSettings,
                    SaveToFolderEnable));
           
            enabledControl.checked(settings.parmIsEnabled());
            FormStringControl folderPathControl = formRun.design().controlName(
                formControlStr(ERFormatDestinationSettings,
                    SaveToFolderPath));
           
            folderPathControl.text(settings.parmFolderPath());
        }
    }
    public void closeOk()
    {
        Object formRunObj = any2Object(this) as Object;
        FormRun formRun = formRunObj;
        if (formRunObj is ERIFormatFileDestinationSettingsStorage)
        {
            ERIFormatFileDestinationSettingsStorage settingsStorage = formRunObj;
            var settings = this.getSaveToFolderSettings(settingsStorage);
           
            FormCheckBoxControl enabledControl = formRun.design().controlName(
                formControlStr(ERFormatDestinationSettings, SaveToFolderEnable));
           
            settings.parmIsEnabled(enabledControl.checked());
           
            FormStringControl folderPathControl = formRun.design().controlName(
                formControlStr(ERFormatDestinationSettings, SaveToFolderPath));
           
            settings.parmFolderPath(folderPathControl.text());
        }
        next closeOk();
    }
    internal ERFormatDestinationSaveToFolderSettings getSaveToFolderSettings(
        ERIFormatFileDestinationSettingsStorage _settingsStorage)
    {
        ERFormatDestinationSaveToFolderSettings ret =
            _settingsStorage.getSettingsByKey(
                ERFormatDestinationSaveToFolderSettings::SettingsKey);
        if (ret == null)
        {
            ret = new ERFormatDestinationSaveToFolderSettings();
            _settingsStorage.addSettings(ret);
        }
        return ret;
    }
}

Build your project with all the newly created and extended elements included . You may refer to ER Configure destinations <https://docs.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/analytics/tasks/er-destinations-2016-11> to get more information how to set up and use different destinations for ER.

Written by: Patrick Sharma, Developer, Microsoft Dynamics 365 F&O, Retail & Commerce

Comments

Popular posts from this blog

Conditionally Hiding Menu Items in D365 FinOps Menus Without Using Feature Keys

In Microsoft Dynamics 365 Finance and Operations (D365 F&O), menu items are the links that can be clicked on to access forms, reports, classes, jobs, and queries. Feature keys are typically used to control the visibility of menu items based on user licenses. However, there are scenarios where you might want to hide menu items conditionally without relying on feature keys.  This can be accomplished by extending the 'SysMenuNavigationObjectFactory' class and modifying the checkAddSubMenu(...) method.  Suppose we want to hide the  Vendor payment journal menu item under Accounts payable > Payments > Vendor payment journal Steps 1. Create an extension of the SysMenuNavigationObjectFactory class [ ExtensionOf ( classStr (SysMenuNavigationObjectFactory))] internal final class SysMenuNavigationObjectFactory_PS_Extension { } 2. Create a Chain of Command (CoC) for the checkAddSubMenu method [ ExtensionOf ( classStr (SysMenuNavigationObjectFactory))] internal final...

Dynamics 365 eCommerce - Setting up the Development Environment

Microsoft Dynamics 365 Commerce is an evolution of Dynamics 365 Retail, which launched with new and improved capabilities for e-commerce, content management. To begin development and extension on Dynamics 365 eCommerce we'll need to install the following tools on the development machine: VSCODE - https://code.visualstudio.com Node.js - https://nodejs.org   (Currently version 10.x is the main supported version and the MSI installer can be found here : https://nodejs.org/dist/latest-v10.x/   ) Yarn - https://legacy.yarnpkg.com Git - https://git-scm.com/downloads (Note that development on eCommerce is only supported on Windows (as at 18/06/20).) Create a folder in your local drive to hold the e-Commerce site code - 'C:\repos' Open CMD in administrator mode and go to the folder directory you just created and clone the  Microsoft eCommerce repository with the following command:  git clone https://github.com/microsoft/Msdyn365.Commerce.Online.git This may take a while....