Wednesday, April 20, 2022

Member not found. (Exception from HRESULT: 0x80020003 (DISP_E_MEMBERNOTFOUND))

Renaming the elements in Dynamics 365 finops results in this error in visual studio.



This might be due to a form design being open on the designer window. Just close the elements in designer window and the error will be gone.

Monday, April 18, 2022

Supressing/comment infolog in Dynamics 365 FinOps(D365FO)

 [ExtensionOf(classStr(ProjStatusType_ReportedFinished))]

final class ProjStatusType_ReportedFinished_Extension

{

    public boolean journalCheckStatus()

    {

        boolean ret;

        SysInfologLevel sysInfologLevel;


        sysInfologLevel = infolog.infologLevel();

        infolog.setInfoLogLevel(SysInfologLevel::None);


        ret = next journalCheckStatus();


        if(!ret)

        {

            infolog.setInfoLogLevel(sysInfologLevel);

        }

        return checkFailed(strfmt("<MyNewlabel>", projTable.ProjId, projTable.Stage()));

    }


}

Tuesday, February 11, 2020

Microsoft.Dynamics.AX.Xpp.ErrorException was thrown


I was getting the above error while invoicing the sales order. The customer wanted to send the sales invoice as an email on print management. Below is print management setup of sales invoice.


Solution:

The issue was the setup on customer default setup on Accounts receivable/Common/Customers/All customers/Setup tab/print management and Accounts receivable/setup/form setup/print management were same. This was triggering the error. It seems report was getting sent to the customer twice.

Tuesday, June 11, 2019

Testing custom services with POSTMAN

Create a POST type entry and enter below in the search bar.

{{resource}}/api/services/<Service Group>/<Service>/<ServiceMethod>

Testing Custom entities in D365FO using POSTMAN scripts

There is a really good article from Microsoft to configure the POSTMAN to test the custom services in D365FO. However there are some small changes here and there that could help you configure the POSTMAN to work better with D365FO.

Few more links below to understand how POSTMAN scripts can be manipulated to fetch the correct results.

vishwad365fo.blogspot.com/2018/05/custom-service-to-create-sales-order.html

axtoday.blogspot.com/2017/08/accessing-dynamics-365-for-operations_17.html


For me even after following the article from Microsoft I was getting error so I did below changes.

After we have generated the Bearer Token and it is added to your environment. Just go to the Authorization tab and select Bearer Token on the Type field. Also copy and paste the Bearer token to the token field.


On the header tab write below values

Authorization:Bearer{{bearerToken}}
Content-Type:application/json



Body tab must select Raw and select JSON from the drop down.



On the Tests Tab put the following JSON script.

var json = JSON.parse(responseBody);
tests["Get  info"] = !json.error && responseBody !== '' && responseBody !== '{}';


Monday, May 13, 2019

Vendor invoice add Requester to the hierarchy workflow in D365FO

I had a request for a development where there was a need to have a hierarchical workflow approval process in Vendor invoice. The issue was the requestor was not available in the workflow setup. To fix this I had the done the below changes.


 Create an extension for table VendInvoiceInfoTable and add new field Originator in table VendInvoiceInfoTable.




 Make sure the EDT for this new field is PurchReqRequesterRefRecId



 Extend query VendInvoiceDocument and add the new field created in table VendInvoiceInfoTable  to this query


Add the field in the form VendEditInvoice


In workflow configuration, you will find requestor, now when invoicing the PO just enter the requester user Id. 

Tuesday, February 26, 2019

Auto fill segmented control on form in D365FO


 public void setSegmentedDefaults()
    {
        DimensionStorage                    dimensionStorage;
        DimensionStorageSegment             segment;   
        DimensionAttribute                  dimensionAttribute;
        DimensionAttributeValue             dimensionAttributeValue;
        ;

        dimensionStorage = DimensionStorage::findById(this.LedgerDimension);

        dimensionAttribute = DimensionAttribute::findByName('BusinessUnit');
        dimensionAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValueNoError(dimensionAttribute, '002', false, true);
        segment = DimensionStorageSegment::constructFromValue('002', dimensionAttributeValue);
        dimensionStorage.setSegmentForHierarchy(1, 2,segment);
        this.LedgerDimension = dimensionStorage.getSavedComboRecId();

        dimensionAttribute = DimensionAttribute::findByName('Department');
        dimensionAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValueNoError(dimensionAttribute, '024', false, true);
        segment = DimensionStorageSegment::constructFromValue('024', dimensionAttributeValue);
        dimensionStorage.setSegmentForHierarchy(1, 3,segment);
        this.LedgerDimension = dimensionStorage.getSavedComboRecId();
                     
    }

Tuesday, February 19, 2019

Call stack window missing in Visual Studio D365FO


It is very crucial to have a call stack window to understand the flow of the code. To enable call stack follow below points.

1. Open visual studio and put a breakpoint.
2. Run the code and wait for the debugger to hit the breakpoint.
3. Go to Debug->windows->Call stack


Monday, February 4, 2019

How to create Retail product category in D365FO using X++


Guys we can use below code to create the retail product category using X++

 EcoResCategory ecoResCatImport

 ecoResCatImport.clear();
 ecoResCatImport.initValue();
 ecoResCatImport.initFromParent(EcoResCategory::findByName('HAL',ecoResCategory.CategoryHierarchy);
ecoResCatImport.Code = 'Test_categoryimport';
 ecoResCatImport.Name = 'Test_Categoryimport';
 ecoResCatImport.addToHierarchy();

Getting Query validation failed-FF004 error in D365FO import job

Once I was importing a package with products V2 entity. I was getting an error "Query validation failed-FF004"  the reason is the data source format in package is set to accept the the row delemeter different from what is set in D365FO.

To resolve this go to

Data Management -> configure data Source -> VerticleBarSaperated(select the source which is mentioned in the manifest file of the packge, in my case it was Vertical Bar Saperated)



Just change the Row delimiter to {LF} form {CRLF} save and import again.

Wednesday, September 19, 2018

Something went wrong while generating the report as an email D365FO

We sometimes get an error message when trying to send email using D365FO.



The issue sometimes is related with the email attached with the user being different from what SMTP server has access to send.

A workaround for this is to change the email for the user to the one which is mentioned in the SMTP setup.



The email mentioned in the email parameter setup can be used to update the user email.




Saturday, June 9, 2018

Know SQL query from entity in D365FO

If we need to find the SQL query from an entity we can create a new query and copy paste the datasource from entity to the new query and write below job to get the SQL query as a string.

class RunnableClasstest
{       
    /// <summary>
    /// Runs the class with the specified arguments.
    /// </summary>
    /// <param name = "_args">The specified arguments.</param>
    public static void main(Args _args)
    {   
        query query;

        queryBuildDataSource    qbds;

        ;

        query = new Query(queryStr(querytest));

       info(query.dataSourceNo(1).toString());
    }

}

Saturday, May 26, 2018

Upload files into Azure Blob in D365FO/AX7

I was tasked with creating a mod where we need to attach files related to products which were coming from Azure Blob, so I created a C# library class to perform this activity. I attached the DLL as a reference to my project and I am calling each method accordingly as per the requirement. 

ConnectionString is nothing but AccountKey which we can get it from Azure Portal where we create the new Blob folder.

Using below code you can Upload,Download, View, Delete files from Azure blob.

using System;
using System.Collections.Generic;
using System.Linq;
//using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using System.IO;
//using Microsoft.WindowsAzure.Storage.Auth;
//using Microsoft.WindowsAzure.Storage.Core;
//using Microsoft.Azure.Management.DataFactories.Models;
//using Microsoft.Azure.Management.DataFactories.Runtime;

namespace BlobsStorageAssembly
{

    using Dynamics.AX.Application;
    public class AccessImageBlob
    {
        public List<string> ListBlobWithStorageClientLibrary(String _subFolder, String _product, String _rootFolder, String _connectionString)
        {
    
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(_connectionString);

            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

            var backupContainer = blobClient.GetContainerReference(_rootFolder);
            var listOfFileNames = new List<string>();
            var list = backupContainer.ListBlobs(_subFolder + "/" + _product, useFlatBlobListing: true);
            foreach (var blob in list)
            {
                listOfFileNames.Add(blob.Uri.ToString());
            }

            return listOfFileNames;
        }

        public void deleteImageFile(String _subFolder, String _product, String _rootFolder, String _connectionString)
        {

            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(_connectionString);

            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

            var backupContainer = blobClient.GetContainerReference(_rootFolder);
            
           
            var list = backupContainer.ListBlobs(_subFolder + "/" + _product, useFlatBlobListing: true);

            if (list == null || list.Count() == 0)
            {
                return;
            }
            foreach (IListBlobItem blob in list)
            {
                String fileName = Path.GetFileName(blob.Uri.ToString());
                ICloudBlob cloudBlob = backupContainer.GetBlobReferenceFromServer(_subFolder + "\\" +fileName,null,null,null);
                cloudBlob.FetchAttributes();
                
                cloudBlob.DeleteIfExists();
            }
           
        }

        public void UploadToAzureStorage(System.IO.Stream streams,String _fileNamePath, String _rootFolder, String _subFolder, String _connectionString)
        {
            
            Task t = this.UploadToAzureStorageInner(streams, _fileNamePath,  _rootFolder,  _subFolder,  _connectionString);
        }

        public async Task UploadToAzureStorageInner(System.IO.Stream streams,String _fileNamePath, String _rootFolder, String _subFolder, String _connectionString)
        {
            string filename;
            CloudStorageAccount storageAccount =
                          CloudStorageAccount.Parse(_connectionString);

            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

            var backupContainer = blobClient.GetContainerReference(_rootFolder);


            Uri uri = new Uri(_fileNamePath);

            filename = System.IO.Path.GetFileName(uri.LocalPath);
            CloudBlockBlob blockBlob = backupContainer.GetBlockBlobReference(_subFolder + "\\" + filename);

            await blockBlob.UploadFromStreamAsync(streams);

        }
    

    }
    
}

Wednesday, April 18, 2018

Database logging issue in D365/AX 7

I have recently come across an issue where I was adding 'currency' field into 'VentTable' for database logging, although the log was getting created the new selected field in setup which was 'currency' was not logging. This was a strange behavior, as the log was getting generated but the change values were blank.

Fix: I analysed and found that sometimes for the database log to work we need to unchecked all the fields from that table (VendTable) and end the setup. Once we are done we must again start the setup and this time add all the required fields including the new field(Currency) we want to add into the database log. This way I was able  to log all the fields.

Thursday, November 9, 2017

How to fetch all the tables associated with a configuration key AX 2012

To fetch all the  tables associated with a particular configuration key we can use below code

static void FindTablesFromConfigKey(Args _args)
{
    // The name of the configuration key to be specified here
    str                     configKeyName   = "Prod";
    Dictionary              dictionary      = new Dictionary();
    ConfigurationKeyId      configKeyId     = dictionary.configurationKeyName2Id(configKeyName);
    TableId                 tableId;
    DictConfigurationKey    dictConfigurationKey;
    DictTable               dictTable;
    container               keyIds;
    int                     i;
    ;

    if (configKeyId)
    {
        // Find all children of the specified configuration key
        for (i = dictionary.configurationKeyNext(0); i; i = dictionary.configurationKeyNext(i))
        {
            dictConfigurationKey = new DictConfigurationKey(i);

            while (dictConfigurationKey.parentConfigurationKeyId())
                dictConfigurationKey = new DictConfigurationKey(dictConfigurationKey.parentConfigurationKeyId());

            if (dictConfigurationKey.id() == configKeyId)
                keyIds += i;
        }

        // Find all tables that have an appropriate configuration key
        i = 0;
        for (tableId = dictionary.tableNext(0);tableId;tableId = dictionary.tableNext(tableId))
        {
            dictTable = new DictTable(tableId);
            if (!dictTable.isMap() && !dictTable.isTmp() && !dictTable.isView())
            {
                if (confind(keyIds, dictTable.configurationKeyId()))
                {
                    i++;
                    info(dictTable.name());
                }
            }
        }
    }

    info(strfmt("%1 tables have configuration key '%2'", i, configKeyName));
}

You can use following configuration keys in the job:
LedgerBasic : General ledger
Bank : Bank
SysAdmin : Administration
LogisticsAdvanced : Logistics
LogisticsBasic : Trade
ProjBasic : Project
QuotationBasic : Quotations
AIF : Application Integration Framework
Currency : Currency
ReportingServices : Reporting Services
COSBaseModule : Cost accounting
Asset : Fixed assets
PBA_ProductBuilder : Product Builder
Req : Master planning
smmOutlook : Microsoft Office Outlook synchronisation
LedgerAdvanced : General ledger - advanced
Prod : Production Series I
WrkCtr : Resources
ProdRouting : Production Series II
SysDevelopmentXPP : X++ development
ProdShop : Production Series III
SMAManagement : Service management
SmmCRM : CRM Series
Event : Event
TradeAgreements : Trade agreements
AIFWebService : Application Integration Framework Web Services
Trv : Expense management
TradeInterCompany : Intercompany
SysDevelopmentMorphX : Development
ProjAdvanced : Project - advanced
WMSBasic : Warehouse Management I
WMSAdvanced : Warehouse Management II
KMBSC : Balanced Scorecard
EP : Enterprise Portal
HRMAdministration : Human Resource I
RFID : RFID
SysDatabaseLog : Database log
CSS : Customer Self-Service
SmmSM : Sales management
BankElectronicBanking : Electronic Banking
ESS : Employee Self-Service
BAS : Business analysis
PurchReq : Purchase Requisition
InventQualityManagement : Quality management
COSPlanCostCalc : Flexible Budgeting
SMASubscription : Subscription
JmgPayroll : Shop Floor Control - Pay generation
HRMCollaborative : Human Resource III
CRSECountry : Country/Regional specific features
JmgJob : Shop Floor Control - Job registration
SIG : Electronic signature
LedgerAdvanced2 : General ledger - advanced II
HRMManagement : Human Resource II
KMBPM : Business Process Management
KMQuestionnaireBasic : Questionnaire I
Jmg : Shop Floor Control
SmmTM : Telemarketing
SmmMA : Marketing automation
KMQuestionnaireAdvanced : Questionnaire II

Database logging using X++ in AX 2012


Use below code to add tables into database logging using X++

The code below will add all the fields of the specific table for database logging. Just replace InventItemSalesSetup with the table you want to use for database logging. You can also put the code in a loop to select group of tables.

static void setDBLogOnTable(Args _args)
{
    TableId tableId = tableNum(InventItemSalesSetup);
    DatabaseLog log;
    SysDictTable dictT = new SysDictTable(tableId);
    SysDictField dictF;
    Set fields;
    SetEnumerator   se;


    log.logType = DatabaseLogType::Update;
    log.logTable = tableId;
    fields = dictT.fields(false,false,true);
    se = fields.getEnumerator();
    while (se.moveNext())
    {
        dictF = se.current();

        log.logField = dictF.id();
        log.insert();
        info(strFmt("Adding field %1", dictF.name()));
    }
    SysFlushDatabaselogSetup::main();
}

Sunday, September 10, 2017

Deleting model files in AX 7





Although it is not recommended to delete the model files in AX 7 however there is a way to delete the model files if you are in development environment or learning AX 7. Just follow below path to delete the model files in AX 7.


1.       You need to stop AOS service by going into IIS manager.




2.       Open Command Prompt and change directory to path C:\AOSService\PackagesLocalDirectory\Bin
To check your packages local directory you can also go to IIS Manager and right click and select explore.



3.       Type the below line and press enter
C:\AOSService\PackagesLocalDirectory\Bin>ModelUtil.exe -delete -metadatastorepath=<Path to package directory> -modelname=<Model Name>

Usage:
C:\AOSService\PackagesLocalDirectory\Bin>ModelUtil.exe -delete -metadatastorepath="C:\AOSService\PackagesLocalDirectory" -modelname="Application suite VAR model"

4.       After deletion of the model you need to delete the package as well.
Go to C:\AOSService\PackagesLocalDirectory and delete the folder with the same name as your model.


5.       Just start AOS service from IIS manager and sync the DB.




Wednesday, September 6, 2017

Tutorial example for modifying Records and Infolog in tables in Dynamics 365 for Operations (AX 7)



To manipulate records in Dynamics 365 for operations we need to follow the below steps:

1. Open visual studio 2015, and create a new Operations Project of type Dynamics 365 for        
        Operations


2. Create a new  Model Test_Model if a model does not exists by clicking on Dynamics 365 in VS 
        menu

3. Check the project properties and make sure you have the setup as below and click apply.
-          Model property should reflect the correct model name
-          Startup Object property should be updated with the new runnable class which we will create in steps below.
1
4. Create a new class by right clicking on the project name ->add->NewItem
On the left page under operations artifacts, select code
On the right select Runnable Class and give a name for the class.


5. Write a simple X++ code to fetch data from BankAccountTable in AX and rebuild the project and click on Run


6. When running you might get an error saying “To run you must set a startup object (Such as a class, form, a menu item).Set this in the Startup project.”

This is because we have not mentioned the correct object name to run from a project, so we need to change this.
Go to Project properties and check if the ‘startup object’ is the name of the job you are trying to run.


7. When running an infolog in Dynamics 365 for operations, AX will open the client page in web browser and the data will be displayed  on the ribbon as shown in below screenshot (Bank Accou). 
- If the data is not displayed correctly, then please check the Company at the top right is the one where the data is.


Friday, March 18, 2016

How to get files from path

I am sharing today a method which can help you get files in a particular path.

static container findMatchingFiles(
        str _folderPath
    ,   str _filePattern   = '*.jpg*')
{
    System.IO.DirectoryInfo     directory;
    System.IO.FileInfo[]        files;
    System.IO.FileInfo          file;
    InteropPermission           permission;
    container                   fileTypes;
    Counter                     counter = 1;

    str         fileName;
    counter     filesCount;
    counter     loop;
    container   mathchingFiles;
    ;

    permission  = new InteropPermission(InteropKind::ClrInterop);
    permission.assert();

    directory   = new System.IO.DirectoryInfo(_folderPath);

    files       = directory.GetFiles(_filePattern);
    filesCount  = files.get_Length();

    fileTypes   = ['*.bmp*','*.jpeg*','*.gif*','*.jpg*','*.png*'];
    while(filesCount == 0 &&  counter <= conLen(fileTypes))
    {
        _filePattern = conPeek(fileTypes,counter);
        files       = directory.GetFiles(_filePattern);
        filesCount  = files.get_Length();
        counter++;
    }


    for (loop = 0; loop < filesCount; loop++)
    {
        file            = files.GetValue(loop);
        fileName        = file.get_FullName();
        mathchingFiles  = conins(mathchingFiles, conlen(mathchingFiles) + 1, fileName);
    }

    CodeAccessPermission::revertAssert();

    return mathchingFiles;
}