18 July 2019

X++ code to write data to Notepad or CSV file in D365FO or AX7

TextStreamIo                        streamIO;
FileName                            fileNameWithExt;
    FileName                            fileName;

 public void initFile()
    {
        #define.validationFileSuffix('-data-validation-errors.txt')
        InteropPermission perm = new InteropPermission(InteropKind::ClrInterop);
       
        perm.assert();
        streamIO = TextStreamIO::constructForWrite(#utf8format);
        streamIO.outRecordDelimiter(#delimiterCRLF);

        fileName += #validationFileSuffix;
    }

boolean validationFailed(FreeTxt _errorTxt)
    {
        boolean ret;

        if(!streamIO)
        {
            this.initFile();
        }
           
        streamIO.write(_errorTxt);

        return ret;
    }


 public void downloadFile()
    {
        if (streamIo != null)
        {
            File::SendFileToUser(streamIo.getStream(), fileName);
        }

        CodeAccessPermission::revertAssert();
    }

10 July 2019

How to calculate Available physical of an item given the inventLocationId that shown on the on hand form


How to get available physical for an Item and for Warehouse in AX 2012 / D365 FO 
InventDimParm       invDimParm;
   InventDim           invDim;
   Qty                 availPhys;

   invDim.InventLocationId = 'INVENTLOCATIONID';
   invDim = InventDim::findOrCreate(invDim);
   invDimParm.initFromInventDim(InventDim::find(invDim.inventDimId));
   availPhys = InventSum::findSum("ITEMID",invDim,invDimParm).availPhysical();
   info(strfmt("availPhys:%1 ",availPhys));
========
InventDim           inventDim;
   InventDimParm       inventDimParm;
   InventOnhand        inventOnhand;
  
   inventDim.InventLocationId = 'Yourwarehouse';
   inventDimParm.initFromInventDim(inventDim);
   inventOnhand = InventOnhand::newParameters('Youritem', inventDim, inventDimParm);
   info(strfmt("Available Physical: %1", inventOnhand.availPhysical()));

08 July 2019

Credit limit check on Sales Quotation based on the customer credit limit in D365 FO of AX7


class TRG_SalesQuotationTableWriteEventHandler
{
    /// <summary>
    ///
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    [FormDataSourceEventHandler(formDataSourceStr(SalesQuotationTable, SalesQuotationLine), FormDataSourceEventType::Written)]
    public static void SalesQuotationLine_OnWriting(FormDataSource sender, FormDataSourceEventArgs e)
    {
        SalesQuotationTable salesQuotationTable;
        SalesQuotationLine  salesQuotationLine,salesQuotationLineLoc;
        CustTransOpenSum    custTransOpenSum;

        salesQuotationLineLoc = sender.cursor();
        salesQuotationTable = salesQuotationTable::find(salesQuotationLineLoc.QuotationId);

        if(smmParametersTable::find().CheckCreditLimitOnQuotation == NoYes::Yes)
        {
            Amount creditLimit  = CustTable::find(salesQuotationLineLoc.CustAccount).CreditMax;
            select sum(LineAmount) from salesQuotationLine
                group by QuotationId
                where salesQuotationLine.QuotationId == salesQuotationTable.QuotationId;
           
            select AccountNum,AmountMST from custTransOpenSum
                where custTransOpenSum.AccountNum == salesQuotationTable.CustAccount;

            Amount newBalance = custTransOpenSum.AmountMST + salesQuotationLine.LineAmount;
            Amount creditExcess = newBalance - creditLimit;
            if(newBalance  > creditLimit)
            {
                warning(strFmt("Quotation:%1 Creit limit exedded Opening balance %2 Current order:%3 New balance:%4 Credit limit:%5 Credit excess:%6 ",
                    salesQuotationLineLoc.QuotationId,
                    custTransOpenSum.AmountMST,
                    salesQuotationLine.LineAmount,
                    newBalance,
                    creditLimit,
                    creditExcess));
            }
        }
    }

}

25 June 2019

Admin provisioning tool can't stop DynamicsAXBatch service

Admin provisioning tool can't stop DynamicsAXBatch service 
System.ComponentModel.Win32Exception: The service cannot accept control messages at this time in D365

  • Click the Start menu
  • Click Run or in the search bar type services.msc
  • Press Enter
  • Look for the service and check the Properties and identify its service name. Service name would be DynamicsAxBatch
  • Once found, open a command prompt. Type: sc queryex [servicename].
  • Press Enter
  • Identify the PID
  • In the same command prompt type: taskkill /f /pid [pid number]
  • Press Enter
  • Now start the service and and register yourself in the Admin Provisioning Tool

21 June 2019

How to access form data source using Form control in the event handler in D365 or AX7

How to access form record using Form control in the event handler in D365 or AX7

[FormControlEventHandler(formControlStr(InventJournalTransfer, Functions), FormControlEventType::Clicked)]

public static void Functions_OnClicked(FormControl sender, FormControlEventArgs e)
    {
        InventJournalTrans      inventJournalTrans;
        InventJournalTable      journalTable;

        FormRun form = sender.formRun();
        FormDataSource journalTable_ds  =       form.dataSource(
        formDataSourceStr(InventJournalTransfer,InventJournalTable)) as FormDataSource;
        journalTable                   = journalTable_ds.cursor();

    }

24 May 2019

How to import Azure bacpac file to Local SQL DB / On premises SQL DB in D365 environment

cd C:\Program Files (x86)\Microsoft SQL Server\140\DAC\bin

SqlPackage.exe /a:import /sf:C:\Users\Adminc4221d5a84\Documents\Backup\AXDBUATCopy07Oct.bacpac /tsn:localhost /tdn:AXDB_UATCOPY07Oct18 /p:CommandTimeout=1200

--or
-- To import to on-premises need to import data using this below link.https://bitwizards.com/thought-leadership/blog/2013/january-2013/restore-sql-azure-database-to-sql-server-2012


CREATE USER axdeployuser FROM LOGIN axdeployuser
EXEC sp_addrolemember 'db_owner', 'axdeployuser'

CREATE USER axdbadmin FROM LOGIN axdbadmin
EXEC sp_addrolemember 'db_owner', 'axdbadmin'

CREATE USER axmrruntimeuser FROM LOGIN axmrruntimeuser
EXEC sp_addrolemember 'db_datareader', 'axmrruntimeuser'
EXEC sp_addrolemember 'db_datawriter', 'axmrruntimeuser'

CREATE USER axretaildatasyncuser FROM LOGIN axretaildatasyncuser
EXEC sp_addrolemember 'DataSyncUsersRole', 'axretaildatasyncuser'

CREATE USER axretailruntimeuser FROM LOGIN axretailruntimeuser
EXEC sp_addrolemember 'UsersRole', 'axretailruntimeuser'
EXEC sp_addrolemember 'ReportUsersRole', 'axretailruntimeuser'

CREATE USER [NT AUTHORITY\NETWORK SERVICE] FROM LOGIN [NT AUTHORITY\NETWORK SERVICE]
EXEC sp_addrolemember 'db_owner', 'NT AUTHORITY\NETWORK SERVICE'

UPDATE T1
SET T1.storageproviderid = 0
    , T1.accessinformation = ''
    , T1.modifiedby = 'Admin'
    , T1.modifieddatetime = getdate()
FROM docuvalue T1
WHERE T1.storageproviderid = 1

ALTER DATABASE [AXDB_UATCOPY07Oct18] SET CHANGE_TRACKING = ON (CHANGE_RETENTION = 6 DAYS, AUTO_CLEANUP = ON)

-- stop the Microsoft dynamics 2 services, Management reporter service, and World wide web service.


ALTER DATABASE [AxDB] MODIFY NAME = [AxDB_Original07Oct18]

ALTER DATABASE [AXDB_UATCOPY07Oct18] MODIFY NAME = [AxDB]

-- start the Microsoft dynamics 2 services, Management reporter service, and World wide web service.
--Do full DB sync
--If it is on premises add a user on admin provision tool to access AX application

.\DeployAllReportsToSsrs.ps1 -LogFilePath C:\logreport.txt -PackageInstallLocation J:\AosService\PackagesLocalDirectory

05 May 2019

D365 FO Report extension example using event handler : Report extension

Report extension in two ways an event handler. Below is the example for the Customer base data report.

Step1. Duplicate the report and do the required changes.
Step2. Create an extension for the Report output MenuItem and output object to new report what we create in the step1.

Or
If the output menuItem assigned with controller class.

Step1. Duplicate the report and do the required changes.
Step2. Create an EventHandler or COC for the Controller class - getReportName() method.
Step3. In the event handler method, assign the new report to return in this method.

 public static str getReportName(Args _args)
    {
        str reportName;

        if (_args.menuItemName() == menuitemOutputStr(CustBasedata))
        {
            reportName = ssrsReportStr(CustBasedata, Report);
        }
        else if (_args.menuItemName() == menuitemOutputStr(CustListReport))
        {
            reportName = ssrsReportStr(CustListReport, Report);
        }
        else if (_args.menuItemName() == menuitemOutputStr(smmSalesCustItemStatistics))
        {
            reportName = ssrsReportStr(smmSalesCustItemStatistics, Report);
        }

        return reportName;
    }


class smmReportController_EventHandler
{
    /// <summary>
    ///
    /// </summary>
    /// <param name="args"></param>
    [PostHandlerFor(classStr(smmReportsController), staticMethodStr(smmReportsController, getReportName))]
    public static void smmReportsController_Post_getReportName(XppPrePostArgs args)
    {
        Str reportName;
        smmReportsController  reportsController = args.getThis();
        Args argsLoc = args.getArg(identifierStr(_args));
        //Args argsLoc = args.getArgNum(1);
        if (argsLoc.menuItemName() == menuitemOutputStr(CustBasedata))
        {
            reportName = ssrsReportStr(CustBasedataTRG, Report);
        }     
        args.setReturnValue(reportName);
    }

}

Service class to get the selected record and deleted matching records and refresh the form data source in D365 F&O

 [DataContractAttribute] class ABCUserProfilesBulkDeleteContract {         UserId userId;     [DataMemberAttribute('UserId')]     pu...