Tuesday, February 28, 2023

Data entity Export using X++ in D365 FO

 Export data entity through X++ in D365 FO.




public final class ExportEntity
{
    public static void main(Args _args)
    {
        #dmf
        Query 				query;
        DMFEntityName 			entityName = "Batch groups";
        SharedServiceUnitFileID 	fileId;       

        // Update query
	// query = new Query(DMFUtil::getDefaultQueryForEntity(entityName));
	query = new query(dmfutil::getDefaultQueryForEntityV3(entityname));
		
        querybuilddatasource qbds = query.datasourcetable(tablenum(BatchGroupEntity));
        sysquery::findorcreaterange(qbds, fieldnum(BatchGroupEntity, ServerId)).value("Batch:DEMO");

        // Export file
        DMFDefinitionGroupName definitionGroupName = 'BatchGroupEntityExport';
		
        try
        {
            DMFEntityExporter exporter = new DMFEntityExporter();

            //There are optional parameters also added
            fileId = exporter.exportToFile(
            entityName,            //Entity label
            definitionGroupName,    //Definition group
            '',                    //ExecutionId group
            'CSV',                 // or 'XML-Element',//Source format to export in
            #FieldGroupName_AllFields,//Specify the field group fields to include in export.
            query.pack(),            //Query criteria to export records
            curExt(),//
            null,                //List of XSLT files
            true,                //showErrorMessages
            false);                //showSuccessMessages

            if (fileId != '')
            {
                //Get Azure blob url from guid
                str downloadUrl = DMFDataPopulation::getAzureBlobReadUrl(str2Guid(fileId));

                System.Uri uri = new System.Uri(downloadUrl);
				
                str fileExt;

                if (uri != null)
                {
                    fileExt = System.IO.Path::GetExtension(uri.LocalPath);
                }

                Filename filename = strFmt('CustomerPaymentData%1',fileExt);
                System.IO.Stream stream = File::UseFileFromURL(downloadUrl);
				
                //Send the file to user
                File::SendFileToUser(stream, filename);
                
                // Below code will delete the export group.
                //DMFDefinitionGroup::find(definitionGroupName, true).delete();
            }
            else
            {
                throw error("The file was not generated succefully. See execution log");
            }
        }
        catch
        {
            throw error("DMF execution failed");
        }

    }
}

Ref: Link


Keep Daxing!!


Monday, February 27, 2023

Upload a file to FTP server using x++.

 Upload and read files from the FTP server using x++.

The file name should not contain spaces and a colon(":").


 Upload file:

static void sendFileToFTP(System.IO.Stream _stream)
{
	System.Text.Encoding 		getUTF8;
	System.Byte[] 			bytes;
	System.Object 			request,response,credential;
	System.Net.FtpWebRequest 	ftpRequest;
	System.IO.Stream 		requestStream;
	System.Net.FtpWebResponse 	ftpResponse;
	Str1260 			ftpFileName = "FTP server" + @"/" + "filename.csv";
	System.IO.StreamReader 		reader;
	System.Char[] 			chars;
	
	/* If file to be created with current data and time we can use this commented code.
            str   timeinstr, executedate;
	    //removed spaces and colons because of FTP protocol voilation and throws error
	    timeinstr = strRem(time2str(timeNow(),DateSeparator::Dot,DateSeparator::Dot)," ");

	    //part of filename
	    executedate = date2Str(today(),213,
				DateDay::Digits2,
				DateSeparator::Hyphen,
			        DateMonth::Digits2,
				DateSeparator::Hyphen,
				DateYear::Digits4);

	    executedate = executedate + "_" + timeinstr + ".CSV";
        */
	
	//new InteropPermission(InteropKind::ClrInterop).assert();
	
	// Convert protect password to normal.
	//cryptoblob2str(WinAPIServer::cryptUnProtectData('Protected Password'))
	
	_stream.Position = 0;
	
	// Encode
	reader 	= new System.IO.StreamReader(_stream);
	getUTF8 = System.Text.Encoding::get_UTF8();
	bytes 	= getUTF8.GetBytes(reader.ReadToEnd());
	
	// Creating request
	request 	= System.Net.WebRequest::Create(new System.Uri(ftpFileName));
	ftpRequest 	= request;
	
	// Creating credentials
	credential = new System.Net.NetworkCredential("username", "password");
	
	// assign parameters to reuest
	ftpRequest.set_Credentials(credential);
	ftpRequest.set_ContentLength(bytes.get_Length());
	ftpRequest.set_Method("STOR");
	ftpRequest.set_Proxy(null);
	
	// Write file to stream
	requestStream = ftpRequest.GetRequestStream();
	requestStream.Write(bytes,0,bytes.get_Length());
	requestStream.Close();
	
	// Get respose
	response = ftpRequest.GetResponse();
	ftpResponse = response;
	
	info('Uploded.');
}


Read file:

public static void ReadFileFromFTP(Str1260 _fileName)
{
    Str1260                         readingPath = 'FTP server';
    System.Object                   ftpRequest;
    System.Net.FtpWebRequest        request;
    System.IO.StreamReader          reader;
    System.Net.NetworkCredential    credential;
    System.Net.FtpWebResponse       response;

    try
    {
	// Convert protect password to normal.
	//cryptoblob2str(WinAPIServer::cryptUnProtectData('Protected Password'))

	// Create request
	ftpRequest      = System.Net.WebRequest::Create(@readingPath + @"/" + _fileName);
	request         = ftpRequest;

	credential      = new System.Net.NetworkCredential('username', 'password');
		
	request.set_Credentials(credential);
		
	response        = request.GetResponse();
	reader          = new System.IO.StreamReader(response.GetResponseStream());
		
	str 	text = reader.ReadToEnd();
    }
    catch
    {
	error("Error");
    }
}

Keep Daxing!!

Generate xml file and upload to blob storage using x++

 Generate XML file and upload to blob storage using x++.


using Microsoft.WindowsAzure.Storage;
Using Microsoft.WindowsAzure.Storage.Blob;

public class uploadXMLToBlobService extends SysOperationServiceBase
{
    System.Byte[]   reportBytes = new System.Byte[0]();
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();

    public void processOperation()
    {	
		XmlDocument doc;
        XmlElement mainNodeXml;
        XmlElement oneChild;
        XmlElement secondChild;

       // #define.filename(@'D:\AnyNew.xml')
        doc = XmlDocument::newBlank();
        mainNodeXml = doc.createElement('HeaderXml');
        doc.appendChild(mainNodeXml);

        oneChild = doc.createElement('1child');
        oneChild.setAttribute('Name','Value');
        mainNodeXml.appendChild(oneChild);


        secondChild = doc.createElement('2child');
        secondChild.appendChild(doc.createTextNode('AccountName'));
        oneChild.appendChild(secondChild);

        //doc.save(#filename);

        reportBytes = enc.GetBytes(doc.outerXml());

        System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(reportBytes);

        CloudBlobDirectory  cloudBlobDirectory;
        CloudBlobClient     cloudBlobClient;
        CloudBlobContainer  cloudBlobContainer;
        CloudStorageAccount  cloudStorageAccount;

        cloudStorageAccount  = CloudStorageAccount::Parse(invoiceParameters.StorageConnection);
        cloudBlobClient      = cloudStorageAccount.CreateCloudBlobClient();
        cloudBlobContainer   = cloudBlobClient.GetContainerReference('Container name');
        CloudBlockBlob  CloudBlockBlob = cloudBlobContainer.GetBlockBlobReference('FilePath + uniqueFilename');
        CloudBlockBlob.UploadFromStream(memoryStream, null, null, null);
    }
}


Keep Daxing!!


Generate Token using x++

 Generate Token using x++.


using System.IO;
using System.Net;
using System.Text;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.File;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Threading.Tasks;

public class Authenticationtoken
{
    public void TokenGen()
    {
        WebException                            exception;
        System.Net.HttpWebRequest               webrequest;
        CLRObject                   		webResponse =null;
        System.Exception            		ex;
        #OCCRetryCount
		
        System.Net.HttpWebResponse response;
        str response1;
        str               	byteStr;
        System.Byte[]           byteArray;
        System.IO.Stream        stream;
        System.IO.Stream        dataStream;
		System.Text.Encoding    utf8;
		
	//Using username and password for Authentication
        byteStr = strfmt('%1:%2', "Username", "PassWord");
		
        webrequest = System.Net.WebRequest::Create("URL") as System.Net.HttpWebRequest;
        
	// Parameters
        webRequest.Method           = 'POST';// POST / Get
        webRequest.KeepAlive        = true;
        webRequest.ContentType      = "application/json";  
        webRequest.ContentLength    = 0;
        //webrequest.Host             ="";
        // webrequest.UserAgent        ="";
        //webrequest.Connection       ="";
        webRequest.Timeout          = maxInt();
		
	//Headers
        System.Net.WebHeaderCollection headers = new System.Net.WebHeaderCollection();
        headers = webRequest.Headers;
		
	// Encoding
        utf8       = System.Text.Encoding::get_UTF8();
        byteArray  = utf8.GetBytes(byteStr);
        byteStr    = System.Convert::ToBase64String(byteArray);
		
        headers.Add("Authorization", 'Basic ' + byteStr);
        //headers.Add("Username", "123");

        if (webRequest != null)
        {
            webResponse = webRequest.GetResponse();

            using (StreamReader streamReader = new StreamReader(webResponse.GetResponseStream()))
            {
               response1 = streamReader.readToEnd();
            }

            webResponse.Close();
        }
        else
        {
            throw new System.Exception("Please provide valid URL");
        }
		
        return Authenticationtoken::deserialize(response1);
    }

    public static void deserialize(str  _json)
    {
        AttendanceAuthenticationContract        attendanceAuthenticationContract;
        
        // List
        //attendanceAuthenticationContract = FormJsonSerializer::deserializeCollection(classNum(AttendanceAuthenticationContract),
        //    _json, Types::Class, classStr(AttendanceAuthenticationContract));

        attendanceAuthenticationContract = FormJsonSerializer::deserializeObject(classNum(AttendanceAuthenticationContract),
            _json);

	//Token
        info(attendanceAuthenticationContract.parmBearerToken());
		
	---------------------or--------------------------------
	Map map = RetailCommonWebAPI::getMapFromJsonString(_json);

        if (map && map.exists('access_token'))
        {
            info(map.lookup('access_token'));
        }
    }

}



Keep Daxing!!


Generate report using x++

 Generate report using x++.


By using the below code I am generating the report and adding it is adding as a attachment to that purchase order.


	Filename                        fileName = 'PurchaseOrder'.pdf';
	SrsReportRunController          controller = new SrsReportRunController();
	PurchaseOrderContract           contract = new PurchaseOrderContract();
	SRSPrintDestinationSettings     settings;
	System.Byte[]                   reportBytes = new System.Byte[0]();
	SRSProxy                        srsProxy;
	SRSReportRunService             srsReportRunService = new SrsReportRunService();
	Microsoft.Dynamics.AX.Framework.Reporting.Shared.ReportingService.ParameterValue[]  parameterValueArray;
	Map                             reportParametersMap;
	SRSReportExecutionInfo          executionInfo = new SRSReportExecutionInfo();
	str                             fileContentType;
	DocuRef                         docuRef;
	DocuValue                       docuValue;
	System.IO.MemoryStream          mstream;
	PurchTable                      purchTable;
	Filename                        attachmentName;

	select firstOnly purchTable
		where purchTable.PurchId == _purchId;

	delete_from docuValue 
		exists join docuRef
		where docuRef.RefRecId  == purchTable.RecId
		&& docuValue.RecId      == docuRef.ValueRecId;

	delete_from docuRef
		where docuRef.RefRecId  == purchTable.RecId;

	//set the contract parameters
	contract.parmPurchId(purchTable.PurchId);

	//set controller parameters
	controller.parmReportName('ReportName');//
	controller.parmShowDialog(false);
	controller.parmLoadFromSysLastValue(false);
	controller.parmReportContract().parmRdpContract(contract);

	// Provide printer settings
	settings = controller.parmReportContract().parmPrintSettings();
	settings.printMediumType(SRSPrintMediumType::File);
	settings.fileName(fileName);
	settings.fileFormat(SRSReportFileFormat::PDF);

	// Below is a part of code responsible for rendering the report
	controller.parmReportContract().parmReportServerConfig(SRSConfiguration::getDefaultServerConfiguration());
	controller.parmReportContract().parmReportExecutionInfo(executionInfo);

	//set proxy and service values
	srsReportRunService.getReportDataContract(controller.parmreportcontract().parmReportName());
	srsReportRunService.preRunReport(controller.parmreportcontract());
	reportParametersMap = srsReportRunService.createParamMapFromContract(controller.parmReportContract());
	parameterValueArray = SrsReportRunUtil::getParameterValueArray(reportParametersMap);
	srsProxy            = SRSProxy::constructWithConfiguration(controller.parmReportContract().parmReportServerConfig());

	// Actual rendering to byte array
	reportBytes = srsproxy.renderReportToByteArray(controller.parmreportcontract().parmreportpath(),
										  parameterValueArray,
										  settings.fileFormat(),
										  settings.deviceinfo());

	//conver the byte array to memory stream
	mstream         = new System.IO.MemoryStream(reportBytes);
	
        
        // To Download 
        Way 1:

        File::SendFileToUser(mstream, fileName);
        
        Way 2:
        attachmentName  = System.IO.Path::GetFileNameWithoutExtension(fileName);
	fileContentType = System.Web.MimeMapping::GetMimeMapping(fileName);
        System.IO.StreamReader sReader = new System.IO.StreamReader(mstream);
fileContent = sReader.ReadToEnd(); File::SendStringAsFileToUser(fileContent, fileName); //create file attachment in purchase order header with blob storage URL docuRef = DocumentManagement::attachFileToCommon(purchTable, DocuType::typeFile(),                                          mstream, fileName, fileContentType, attachmentName); //Modify the attachment's custom field details if(docuRef.RecId) { }





Keep Daxing!!

Wednesday, February 22, 2023

Export Financial Dimensions using x++

 Export Financial Dimensions using x++. 

Tables list is mentioned below.


public class ExportFinancialDimensionService extends SysOperationServiceBase
{

    public void processOperation()
    {
        DimAttributeMainAccount     dimAttributeMainAccount;
        DimAttributeOMCostCenter    dimAttributeOMCostCenter;
        DimAttributeProjTable       dimAttributeProjTable;
        DimAttributeAssetTable      dimAttributeAssetTable;
        DimAttributeOMDepartment    dimAttributeDepartment;
        DimAttributeOMBusinessUnit  dimAttributeBusinessUnit;
        DimAttributeVendTable       dimAttributeVendTable;
        DimAttributeCustTable       dimAttributeCustTable;

	// BusinessUnit
	while select dimAttributeBusinessUnit
	{
	    info(Strfmt('%1 - %2',dimAttributeBusinessUnit.Value, dimAttributeBusinessUnit.Name);
	}

	DimensionFinancialTag  			siteDimensionFinancialTag;
	DimensionAttributeDirCategory   dimensionAttributeDirCategory;

	// Site
	while select siteDimensionFinancialTag
	    join dimensionAttributeDirCategory
	        where siteDimensionFinancialTag.FINANCIALTAGCATEGORY == dimensionAttributeDirCategory.DirCategory
                && dimensionAttributeDirCategory.DimensionAttribute == DimensionAttribute::findByName(#Site).RecId
	{
	    info(Strfmt('%1 - %2',siteDimensionFinancialTag.Value, siteDimensionFinancialTag.Description));
	}
		
	// Department
	while select dimAttributeDepartment
	{
	    info(Strfmt('%1 - %2',dimAttributeDepartment.Value, dimAttributeDepartment.Name);
	}
		
	// MainAccount
	while select dimAttributeMainAccount 
	{
	    info(Strfmt('%1 - %2',dimAttributeMainAccount.Value, dimAttributeMainAccount.Name);
	}

	//Assets
	while select dimAttributeAssetTable
	{
	    info(Strfmt('%1 - %2',dimAttributeAssetTable.Value, dimAttributeAssetTable.Name);
	}

	// Customers
	while select dimAttributeCustTable
	{
	    info(Strfmt('%1 - %2',dimAttributeCustTable.Value, dimAttributeCustTable.Name));
	}
    }
}


Keep Daxing.



update custom fields in vendInvoiceInfoTable from PurchTable while invoicing the purchase order in D365FO

 update custom fields in vendInvoiceInfoTable from PurchTable while invoicing the purchase order in D365FO. While invoicing the purchase order system will create a pending vendor invoice.

I have added a few custom fields in the purchase order header and purchase order line. So, I need to transfer the data from PurchTable to vendInvoiceInfoTable

Check the below code.


For line fields:

[ExtensionOf(classStr(PurchFormletterParmData))]
internal final class PurchFormLetterParmData_Extension
{
    protected void createParmLineRecord(VendDocumentLineMap purchParmLine, PurchLine purchLine, 
					VendDocumentLineAssetMap purchParmLine_Asset, TradeLineRefId tableRef, 
					container quantities, container cwQuantities)
    {
        if (_purchParmLine.TableId == tableNum(VendInvoiceInfoLine))
        {
            VendInvoiceInfoLine vendInvoiceInfoLine = _purchParmLine;

            vendInvoiceInfoLine.Field1  = _purchLine.Field1;
            vendInvoiceInfoLine.Field2  = _purchLine.Field2;
        }
        
        next createParmLineRecord(_purchParmLine, purchLine, purchParmLine_Asset, 
						tableRef, quantities, _cwQuantities);
    }

}


For Header and line fields:

[ExtensionOf(classStr(PurchFormletterParmDataInvoice))] 
final class VendInvoiceInfoLine_Extension 
{ 
   // Header fields update
   protected void createInvoiceHeaderFromTempTable() 
   { 
      vendInvoiceInfoTable vendInvoiceInfoTable; 
      PurchTable           purchtable; 

       next createInvoiceHeaderFromTempTable(); 

       select forupdate vendInvoiceInfoTable 
           where vendInvoiceInfoTable.ParmId == this.parmId(); 
           //&& vendInvoiceInfoTable.PurchId == purchtable.PurchId; 
            
       purchtable = PurchTable::find(vendInvoiceInfoTable.PurchId); 

       // Code
       ttsbegin; 
       vendInvoiceInfoTable.Field2 	= purchtable.Field2; 
       vendInvoiceInfoTable.Field2 	= purchtable.Field2; 
       vendInvoiceInfoTable.update(); 
       ttscommit; 

    } 
  
   // Line fields update
   protected void createParmLinesAndTable() 
   { 
       VendInvoiceInfoLine vendInvoiceInfoLine; 
       PurchLine           purchline; 
	   
       next createParmLinesAndTable(); 

       while select forupdate vendInvoiceInfoLine 
           where vendInvoiceInfoLine.ParmId == this.parmId() 
       { 
            purchline = PurchLine::findRecId(vendInvoiceInfoLine.PurchLineRecId); 
		
            // Code
            ttsbegin; 
	    vendInvoiceInfoLine.Field1 = purchline.Field1; 
            vendInvoiceInfoLine.Field2 = purchline.Field2; 
	    vendInvoiceInfoLine.update(); 
	    ttscommit; 
       } 

   } 
} 


Keep Daxing!!

Connect SQL using X++ Code with ODBC

 Connect SQL using X++ Code with ODBC.


Control panel -> Administration -> 


	LoginProperty 	loginProperty;
	OdbcConnection 	odbcConnection;
	Statement 		statement;
	ResultSet 		resultSet;
	str 			sql, criteria;
	SqlStatementExecutePermission perm;

	str                             strConnectionString;
	str                             dbServer    = 'Server name';
	str                             dsnUser     = 'User name';
	str                             dsnUSerPwd  = 'Password';

	strConnectionString = strfmt("UID=%1;PWD=%2",dsnUser,dsnUSerPwd);
	strConnectionString = strfmt("DSN=%1;UID=%2;PWD=%3",'TestIntegration', dsnUser,dsnUSerPwd);

	// Set the information on the ODBC.
	loginProperty = new LoginProperty();
	// loginProperty.setDSN("TestIntegration");
	loginProperty.setServer(dbServer);
	loginProperty.setDatabase("TIntegration");
	loginProperty.setOther(strConnectionString);

	//Create a connection to external database.
	odbcConnection = new OdbcConnection(loginProperty);

	if (odbcConnection)
	{
		// Insert data 
		sql ="INSERT INTO [TestTable](FIELD1,FIELD2) VALUES";
		
		sql = sql + "('"+'1234511'+"','"+'Test1231'"')";

		perm = new SqlStatementExecutePermission(sql);
		perm.assert();
		statement = odbcConnection.createStatement();
		statement.executeUpdate(sql);
		statement.close();
			
		/*
		// getting data
		sql = "SELECT top 1 FIELD1 FROM TestTable where field2 = ";
		+ criteria
		+ " ORDER BY FIELD1, FIELD2 ASC ;";
		

		//Assert permission for executing the sql string.
		perm = new SqlStatementExecutePermission(sql);
		perm.assert();

		//Prepare the sql statement.
		statement = odbcConnection.createStatement();
		resultSet = statement.executeQuery(sql);


		while (resultSet.next())
		{
			Info(strFmt('%1', resultSet.getString(1)));
			Info(strFmt('%1', resultSet.getString(3)));
		}

		//Close the connection.
		resultSet.close();
		statement.close();*/
	}
	else
	{
		error("Failed to log on to the database through ODBC.");
	}


Keep Daxing!!

Trigger URL with simple json using x++

 Trigger URL with simple json using x++.


	RetailCommonWebAPI      retailCommonWebAPI = RetailCommonWebAPI::construct();

	str requestURL = 'URL';
	str json = '{"TestField" : "123"}';

	retailCommonWebAPI.makePostRequest(requestURL, json, '', 'application/json');


Keep Daxing!!

Multithread based on user count in sysoperation framework in D365FO

 Multithread based on user count in sys operation framework in D365FO.

  • The system will create multiple tasks in the same batch job id.
  • Based on user input system will create multiple threads.

Multithread Contract Class :

[DataContractAttribute]
class TestMultiThreadContract
{
    private int    tasksNumber;

    [DataMemberAttribute('Count')]
    public int parmParallelTasksNumber(int _tasksNumber = tasksNumber)
    {
	tasksNumber = _tasksNumber;
	return tasksNumber;
    }
}


Multithread Controller class: 

class TestMultiThreadController extends SysOperationServiceController
{
    protected void new()
    {
        super();

        this.parmClassName(classStr(openSalesOrderService));
        this.parmMethodName(methodStr(openSalesOrderService, createScheduleTask));
        this.parmExecutionMode(SysOperationExecutionMode::ScheduledBatch);
        this.parmDialogCaption("Sales order process");
    }

    public static TestMultiThreadController construct()
    {
        return new TestMultiThreadController();
    }

    public static void main(Args args)
    {
        TestMultiThreadController::construct().startOperation();
    }

    public ClassDescription defaultCaption()
    {
        return "Sales order process";
    }
}


Multithread Service Class :

class TestMultiThreadService extends SysOperationServiceBase
{
    int                         numberOfOpenRecord;
    int                         numberOfBatchThread;
    FromDate                    fromDateFilter;
    ToDate                      toDateFilter;
    Integer                     numberOfBundleSize;

    public void createScheduleTask(TestMultiThreadContract _contract)
    {
        Integer             eachRecordCount;
        SalesId             fromSalesId,
                            toSalesId,
                            lastSalesId;
        FromDate            fromDateId;
        ToDate              toDateId;

        if (!this.isExecutingInBatch())
        {
            throw error(Error::wrongUseOfFunction(funcName()));
        }
        
        numberOfBatchThread = _contract.parmParallelTasksNumber();
		
        numberOfOpenRecord  = this.openSalesOrderCount();

        if (numberOfOpenRecord > 0 && numberOfBatchThread > 0)
        {
            BatchHeader batchHeader = this.getCurrentBatchHeader();
			
			// how many bundle will run thay will given by user.
            numberOfBundleSize = numberOfOpenRecord div numberOfBatchThread;

            SalesTable       salesTable;

            while select SalesId from salesTable
                order by salesTable.SalesId
                where salesTable.SalesStatus == SalesStatus::Backorder
            {
                eachRecordCount++;

                if (eachRecordCount == 1)
                {
                    fromSalesId = salesTable.SalesId;
                }

                if (eachRecordCount == numberOfBundleSize)
                {
                    toSalesId = salesTable.SalesId;

                    TestContract 	contract;
                   
                    var controller = SysOperationServiceController((classStr(TestMyService),
								    methodStr(TestMyService, processOperation),
								    SysOperationExecutionMode::Synchronous));
					 contract = controller.getDataContractObject();
					 contract.parmFromSalesOrderNum(fromSalesId);
                     contract.parmToSalesOrderNum(toSalesId);																			
																								
                    batchHeader.addRuntimeTask(controller, this.getCurrentBatchTask().RecId);
                    
                    eachRecordCount = 0;
                }

                lastSalesId = salesTable.SalesId;
            }

            if (eachRecordCount > 0)
            {
                toSalesId = lastSalesId;

                TestContract 	contract;
                   
		var controller = SysOperationServiceController((classStr(TestMyService),
								methodStr(TestMyService, processOperation),
								SysOperationExecutionMode::Synchronous));
		contract = controller.getDataContractObject();
		contract.parmFromSalesOrderNum(fromSalesId);
		contract.parmToSalesOrderNum(toSalesId);																			
																							
		batchHeader.addRuntimeTask(controller, this.getCurrentBatchTask().RecId);
            }

            batchHeader.save();
        }
    }

    private int openSalesOrderCount()
    {
        SalesTable          salesTable;

        select count(RecId) from salesTable
            where salesTable.SalesStatus == SalesStatus::Backorder;

	numberOfOpenRecord = int642int(salesTable.RecId);

        return numberOfOpenRecord;
    }
}


My Service Class :

class TestMyService extends SysOperationServiceBase
{
    public void AutoPostProcessExecute(TestContract _contract)
    {
        #OCCRetryCount
        if (!this.isExecutingInBatch())
        {
            throw error(Error::wrongUseOfFunction(funcName()));
        }

        SalesId             fromSalesId     = _contract.parmFromSalesOrderNum();
        SalesId             toSalesId       = _contract.parmToSalesOrderNum();


        SalesTable      salesTable;
        int             counter;

        while select salesTable
            order by salesTable.SalesId
            where salesTable.SalesId >= fromSalesId
            && salesTable.SalesId <= toSalesId
            && salesTable.SalesStatus == SalesStatus::Backorder
        {
	    try
	    {
		ttsbegin;
		this.insertSalesOrderIntoCustomTable(salesTable);
		counter++;
		ttscommit;
	    }
	    catch (Exception::Deadlock)
	    {
	    	retry;
	    }
	    catch (Exception::UpdateConflict)
	    {
		if (appl.ttsLevel() == 0)
		{
	    	    if (xSession::currentRetryCount() >= #RetryNum)
		    {
			throw Exception::UpdateConflictNotRecovered;
		    }
		    else
		    {
			retry;
		    }
		}
		else
		{
		    throw Exception::UpdateConflict;
		}
	    }
	    catch(Exception::DuplicateKeyException)
	    {
		if (appl.ttsLevel() == 0)
		{
	    	    if (xSession::currentRetryCount() >= #RetryNum)
		    {
			throw Exception::DuplicateKeyExceptionNotRecovered;
		    }
		    else
		    {
			retry;
		    }
		}
		else
		{
		    throw Exception::DuplicateKeyException;
		}
	    }
	    catch (Exception::Error)
	    {
		infolog.add(Exception::Error, strFmt("", SalesTable.SalesId));
							
		continue;
	    }
	    catch (Exception::CLRError)
	    {
		System.Exception ex = CLRInterop::getLastException();
		Infolog.add(Exception::CLRError,  ex.ToString() );

		continue;
	    }
        }
    }
    info(strFmt("%1 Sales order invoiced", counter));
}

 
    private void insertSalesOrderIntoCustomTable(SalesTable _salesTable)
    {
	MycustomTable	mycustomTable;
		
	mycustomTable.salesId 		= _salesTable.salesId;
	mycustomTable.CustomField1 	= _salesTable.CustomField1;
	mycustomTable.CustomField2 	= _salesTable.CustomField2;
	mycustomTable.insert();
    }
}


My ContractClass :

[DataContractAttribute]
class TestContract
{
    private SalesId    fromSalesId;
    private SalesId    toSalesId;

    [DataMemberAttribute]
    public SalesId parmFromSalesOrderNum(SalesId _fromSalesId = fromSalesId)
    {
        fromSalesId = _fromSalesId;
        return fromSalesId;
    }

    [DataMemberAttribute]
    public SalesId parmToSalesOrderNum(SalesId _toSalesId = toSalesId)
    {
        toSalesId = _toSalesId;
        return toSalesId;
    }
}


Using RunBase Batch Click here

Keep Daxing!!






Thursday, February 16, 2023

Sales invoice journal create and posting classes in D365FO

 Sales invoice journal create and posting classes in D365FO.

While creating:

[ExtensionOf(classstr(SalesInvoiceJournalCreateBase))]
final class SalesInvoiceJournalCreateBase_Extension
{
    public InvoiceId SalesInvoiceNum;

    protected void initJournalHeader()
    {       
        next initJournalHeader();

        if(CustParameters::find().ManualSalesInvoiceId == NoYes::Yes)
        {
            custInvoiceJour custInvoiceJourLocal;
            
            if(!SalesInvoiceNum)
            {            
                warning("Invoice number must be filled in.");
                throw error(strFmt("@SYS26498", custInvoiceJour.SalesId));          
            }
            select firstonly SalesId,InvoiceId from custInvoiceJourLocal
                where custInvoiceJourLocal.InvoiceId == SalesInvoiceNum;

            if(custInvoiceJourLocal.RecId != 0)
            {                
                warning(strFmt("Invoice number %1 already used for sales order %2",
                    SalesInvoiceNum,custInvoiceJourLocal.SalesId));
                throw error(strFmt("@SYS26498", custInvoiceJour.SalesId));
            }

            custInvoiceJour.InvoiceId = SalesInvoiceNum;
        }
    }

    protected container getNumAndVoucher()
    {
        container conLocal = next getNumAndVoucher();

        if(CustParameters::find().ManualSalesInvoiceId == NoYes::Yes)
        {
            SalesInvoiceNum = salesParmUpdate.InvoiceId;
            conLocal = conPoke(conLocal,1,SalesInvoiceNum);
        }

        return conLocal;
    }

}

While Posting:

[ExtensionOf(classstr(CustPostInvoice))]
final class CustPostInvoice_Extension
{
    public CustInvoiceId CustInvoiceId;

    protected container getInvoiceIdAndVoucher(CustInvoiceTable _custInvoiceTable, NumberSeq _numberSeq)
    {
        CustInvoiceTable custInvoiceTableLocal;

        container conLocal = next getInvoiceIdAndVoucher(_custInvoiceTable,_numberSeq);

        if(CustParameters::find().ManualSalesInvoiceId == NoYes::Yes)
        {
           CustInvoiceId = _custInvoiceTable.InvoiceId;

            if(!CustInvoiceId)
            {
                warning("Invoice number must be filled in.");
                throw error("Posting has been cancelled.");
            }
            select firstonly InvoiceId from custInvoiceTableLocal
                where custInvoiceTableLocal.InvoiceId == CustInvoiceId;

            if(custInvoiceTableLocal.RecId != 0)
            {
                warning(strFmt("Invoice number %1 has already been used.", CustInvoiceId));
                throw error("Posting has been cancelled.");
            }

            conLocal = conPoke(conLocal,1,CustInvoiceId);
        }


        return conLocal;
    }

}


Keep Daxing!!

Trigger business event on workflow approval process in D365FO

 Trigger business event for the workflow approval process in D365FO.

Got a requirement to do the approval process without clicking the Approve button. upon user submission, the record transitions to a 'Submitted' state and automatically triggers a business event without the need for manual approval. The approver will receive the email notification to Approve.


Way 1: Using WorkflowWorkItemTable

[ExtensionOf(tableStr(WorkflowWorkItemTable))]
final class WorkflowWorkItemTable_Extension
{  
    public void insert()
    {
        PurchTable              purchTable;
        WorkflowWorkItemTable   workflowStatusLoc;
        WorkflowElementTable    workflowElementTable;

        next insert();

        select firstonly workflowElementTable
            where workflowElementTable.ElementId == this.ElementId;

        if (workflowElementTable && !strContains(strUpr(workflowElementTable.Name),"REVIEW"))
        {
            if (this.CompanyId)
            {     changecompany(this.CompanyId)     {     select firstonly PurchId from purchTable where purchTable.RecId == this.RefRecId && purchTable.TableId == this.RefTableId;     if(purchTable.PurchId)     {     //BusinessEvent class code     }                 } }             else             {                 PurchReqTable purchReqTable;// Global table
                select firstonly PurchReqId from purchReqTable
                 where purchReqTable.RecId == this.RefRecId
                  && purchReqTable.TableId == this.RefTableId;
    if(purchReqTable.PurchReqId)
    {                         //BusinessEvent class code     }             } } }     // While Reassign this method will trigger public void update() { PurchTable purchTable; WorkflowWorkItemTable workflowStatusLoc; WorkflowElementTable workflowElementTable; next update(); if(this.Status == WorkflowWorkItemStatus::Pending) { select firstonly workflowElementTable where workflowElementTable.ElementId == this.ElementId; if(workflowElementTable && !strContains(strUpr(workflowElementTable.Name),"REVIEW")) { select firstonly PurchId from purchTable where purchTable.RecId == this.RefRecId && purchTable.TableId == this.RefTableId; if(purchTable.PurchId) { //BusinessEvent class code } } } }


Way 2: Using WorkflowTrackingTable

    // It will trigger while Once Approve process starts. 
    [DataEventHandler(tableStr(WorkflowTrackingTable), DataEventType::Inserted)]
    public static void WorkflowTrackingTable_onInserted(Common sender, DataEventArgs e)
    {
        WorkflowTrackingTable   workflowTrackingTable = sender;

        if (workflowTrackingTable.TrackingContext == WorkflowTrackingContext::Approval 
                && workflowTrackingTable.TrackingType == WorkflowTrackingType::Creation)
        {
            WorkflowTrackingStatusTable        workflowTrackingStatusTable;

            select workflowTrackingStatusTable 
                where workflowTrackingStatusTable.RecId == workflowTrackingTable.WorkflowTrackingStatusTable;

            PurchTable              purchTable;

            select firstonly PurchId from purchTable
                where   purchTable.RecId    == workflowTrackingStatusTable.ContextRecId
                &&      purchTable.TableId  == workflowTrackingStatusTable.ContextTableId;

            if(purchTable.PurchId)
            {
                info ('Triggered');
            }
        }
    }



Keep Daxing!!






Tuesday, February 14, 2023

Trigger the API using X++

 Trigger the API using X++.


using System.IO;
using System.Net;
using System.Text;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.File;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Threading.Tasks;

    Way 1 : With Encoding the JSON

public void callAPI()
{
    WebException                exception;
    HttpWebRequest              webRequest;
    WebResponse                 webResponse;
    CLRObject                   clrObj;
    System.Exception            ex;
    System.Byte[]               bytes;
    System.Text.Encoding        encoding;
    #OCCRetryCount
        

    webRequest                  = System.Net.WebRequest::Create('URL');
    webRequest.Method           = 'MethodName';// POST / Get
    webRequest.KeepAlive        = true;
    webRequest.ContentType      = 'ContentType';//"application/json"
    webRequest.Timeout          = maxInt();

    System.Net.WebHeaderCollection headers = new System.Net.WebHeaderCollection();

    Headers = webRequest.Headers;
    headers.Add('Authorization', 'Bearer ' + token);

	// With Encoding
    if (reqJson)
    {
        encoding    = System.Text.Encoding::get_UTF8();
        bytes       = encoding.GetBytes(reqJson);

        webRequest.ContentLength = bytes.get_Length();

        using (Stream stream = webRequest.GetRequestStream())
        {
            stream.Write(bytes,0,bytes.get_Length());
            stream.Close();
        }
    }

    if (webRequest != null)
    {
        webResponse = webRequest.GetResponse();

        using (StreamReader streamReader = new StreamReader(webResponse.GetResponseStream()))
        {
            str response = streamReader.readToEnd();
        }

        webResponse.Close();

    }
    else
    {
        throw new System.Exception("Please provide valid URL");
    }
}

    Way 2 : Without Encoding the JSON

public void callAPI()
{
    System.Net.HttpWebRequest       request;
    System.Net.HttpWebResponse      response;
    CLRObject                       clrObj;
    System.Exception                ex;
    System.Net.WebHeaderCollection  httpHeader;
    str                             requestJson, responseJson;

    try
    {
        // We can use commented code also
        //new InteropPermission(InteropKind::ClrInterop).assert();
        //clrObj = System.Net.WebRequest::Create('Url');
        //request = clrObj;

        request  = System.Net.WebRequest::Create('URL');
        request.Method      = "POST";
        request.ContentType = "application/json";
           
        // Adding header in 2 ways 
        httpHeader = new System.Net.WebHeaderCollection();

		// Way 1: Get the header from request
        httpHeader = request.Headers;
        httpHeader.Add("Authorization", 'Token');
        httpHeader.Add('key:' + 'Key');//If your API need any mandatory tags that time this is needed. 
		
		//  Way 2: assign header to request
        //request.set_Headers(httpHeader);

        //Upload Json
        //System.IO.Stream    requestStream = request.GetRequestStream();
        using (System.IO.StreamWriter   streamWriter = new System.IO.StreamWriter(request.GetRequestStream()))
        {
            streamWriter.Write(requestJson); // writing JSON
            streamWriter.Flush();
            streamWriter.Close();
        }
        if (request != null)
        {
            response = request.GetResponse();
            using (System.IO.StreamReader responseStream = new System.IO.StreamReader(response.GetResponseStream()))
            {
                responseJson = responseStream.ReadToEnd().ToString();

                info(responseJson);
            }
        }
    }
}


https://msdax.wordpress.com/2018/04/30/ax-2012-call-restful-api-using-basic-authentication-x-dynamics-ax-2012/

Keep Daxing!!

Get the environment link Using x++

 Get the environment link Using x++.


Microsoft.Dynamics.ApplicationPlatform.Environment.IApplicationEnvironment     env;
         
env = Microsoft.Dynamics.ApplicationPlatform.Environment.EnvironmentFactory::GetApplicationEnvironment();

//return env.Infrastructure.FullyQualifiedDomainName;
return env.Infrastructure.HostUrl;


Keep Daxing!!

Friday, February 10, 2023

Upload and download file from Blob Storage using x++

 Upload and download file from Blob Storage using x++.

By using below code we connect to Blob storage. Below operations also we can do it.

  •     File List
  •     Read the file content
  •     Move the file between folder.
  •     Upload File
  •     Delete File

using Microsoft.WindowsAzure.Storage;
Using Microsoft.WindowsAzure.Storage.Blob;
using System.IO;
class TestAzureBlob 
{
    public static void main(Args _args)
    {
        TestAzureBlob       testAzureBlob = new TestAzureBlob();
        CloudBlobContainer  blobContainer;

        // Connecting container
        blobContainer = testAzureBlob.connectToAzureBlob();

        // Get's the file List
        testAzureBlob.getFilesList(blobContainer);// Way 1
        testAzureBlob.readTheFiles(blobContainer);// Way 2

        // Read the data in file
        testAzureBlob.readFileValue(blobContainer);

        // upload the file
        testAzureBlob.UploadFileToBlob(blobContainer);

        // move the file
        testAzureBlob.moveTheFileFromOneFolderToAnotherFolder(blobContainer);

        // delete the file
        testAzureBlob.deleteFileFromFolder(blobContainer);
    }
}


    Connect to Blob:

    public CloudBlobContainer connectToAzureBlob()
    {
        CloudBlobClient 	cloudBlobClient;
        CloudBlobContainer	cloudBlobContainer;
        CloudStorageAccount cloudStorageAccount;
  
        cloudStorageAccount = CloudStorageAccount::Parse("Azure Blob Connection String");
        // Example :
        // ("DefaultEndpointsProtocol = https;
        // AccountName = 'AccountName';AccountKey = 'AccountKey';EndpointSuffix=core.windows.net");
        cloudBlobClient 	= cloudStorageAccount.CreateCloudBlobClient();
        cloudBlobContainer 	= cloudBlobClient.GetContainerReference("mycontainer");
  
        Info(cloudBlobContainer.Name);

        return cloudBlobContainer;
    }

    Get the files List:

    Way 1:

     public void getFilesList(CloudBlobContainer _cloudBlobContainer)
    {
        // Directory of blob container
        CloudBlobDirectory  cloudBlobDirectory;
        container           fileCon;

        // Folder Path
        cloudBlobDirectory = _cloudBlobContainer.GetDirectoryReference("My Folder");

        System.Collections.IEnumerable lstEnumarable = cloudBlobDirectory.ListBlobs(false, 0, null, null);
        System.Collections.IEnumerator lstEnumarator = lstEnumarable.GetEnumerator();

        List filenames = new List(Types::String);

        while(lstEnumarator.MoveNext())
        {
            IListBlobItem item = lstEnumarator.Current;

            if (item is CloudBlockBlob)
            {
                CloudBlockBlob      blob = item;

                blob.FetchAttributes(null, null, null);

                fileCon = str2con(blob.name, "/");

                filenames.addStart(conPeek(filecon, conLen(filecon)));

                info(strFmt("File : %1", conPeek(filecon, conLen(filecon))));
            }
        }
    }

    Way 2:

    public void readTheFiles(CloudBlobContainer _cloudBlobContainer)
    {
        System.Collections.IEnumerable lstEnumarable = _cloudBlobContainer.ListBlobs(null, false, 0, null, null);
        System.Collections.IEnumerator lstEnumarator = lstEnumarable.GetEnumerator();

        List filenames = new List(Types::String);
        while (lstEnumarator.MoveNext())
        {
            IListBlobItem item = lstEnumarator.Current;

            if (item is CloudBlockBlob)
            {
                CloudBlockBlob blob = item;
                System.IO.StreamReader  reader = new System.IO.StreamReader(blob.OpenRead(null, null, null));
                Info(reader.ReadToEnd());
            }
        }
    }

    Read the file content:

    public void readFileValue(CloudBlobContainer _cloudBlobContainer)
    {
        CloudBlobDirectory  cloudBlobDirectory  = _cloudBlobContainer.GetDirectoryReference("My Folder");
        CloudBlockBlob      blob                = cloudBlobDirectory.GetBlockBlobReference("File");

        System.IO.Stream memory = blob.openRead(null, null, null);

        // Read the content
        System.IO.StreamReader streamReader = new System.IO.StreamReader(memory);

        // Read each line
        str  strRecord = streamReader.ReadLine();

        // while (!System.String::IsNullOrEmpty(strRecord))
        {
            try
            {
                container   conRecord = str2con_RU(strRecord, ',');

                info(conPeek(conRecord, 1));

                info(conPeek(conRecord, 2));

                strRecord = streamReader.ReadLine();
            }
            catch
            {
                throw error("Message");
            }
        
        }
    }

    Upload the file:

     public void UploadFileToBlob(CloudBlobContainer _cloudBlobContainer)
    {
        CloudBlobDirectory      cloudBlobDirectory;
        CloudBlockBlob          CloudBlockBlob;
        System.Byte[]           reportBytes = new System.Byte[0]();

        // File path
        cloudBlobDirectory  = _cloudBlobContainer.GetDirectoryReference("My Folder");
        CloudBlockBlob      = cloudBlobDirectory.GetBlockBlobReference("File.Txt");

        // ---------------- or ----------------
        CloudBlockBlob      = _cloudBlobContainer.GetBlockBlobReference("File.Txt");

        // Encode
        System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
        reportBytes = enc.GetBytes("YOUR XML STRING/TEXT or Data in file");
        System.IO.MemoryStream stream = new System.IO.MemoryStream(reportBytes);
 
        // upload stream
        CloudBlockBlob.UploadFromStream(stream, null, null, null);
    }

    Move the file:

    public void moveTheFileFromOneFolderToAnotherFolder(CloudBlobContainer _cloudBlobContainer)
    {
        CloudBlobDirectory      sourceCloudBlobDirectory;
        CloudBlobDirectory      destinationCloudBlobDirectory;
        CloudBlockBlob          sourceCloudBlockBlob;
        CloudBlockBlob          destinationCloudBlockBlob;

        sourceCloudBlobDirectory        = _cloudBlobContainer.GetDirectoryReference("My Folder");
        destinationCloudBlobDirectory   = _cloudBlobContainer.GetDirectoryReference("My Folder 2");

        sourceCloudBlockBlob            = sourceCloudBlobDirectory.GetBlockBlobReference("File1.Txt");
        destinationCloudBlockBlob       = destinationCloudBlobDirectory.GetBlockBlobReference("File1.Txt");

        // Upload the file to destination
        destinationCloudBlockBlob.UploadFromStream(sourceCloudBlockBlob.OpenRead(null, null, null), null, null, null);
    }

    Delete the file:

    public void deleteFileFromFolder(CloudBlobContainer _cloudBlobContainer)
    {
        CloudBlobDirectory      sourceCloudBlobDirectory;
        CloudBlockBlob          sourceCloudBlockBlob;

        sourceCloudBlobDirectory        = _cloudBlobContainer.GetDirectoryReference("My Folder");
        sourceCloudBlockBlob            = sourceCloudBlobDirectory.GetBlockBlobReference("File2.Txt");

        // Delete file
        sourceCloudBlockBlob.delete(0, null, null, null);
    }


Keep Daxing!!

Thursday, February 9, 2023

Vendor Undo Settlement using X++:

 Vendor Undo Settlement using X++:



final class RunnableClass
{
    public static void main(Args _args)
    {
        VendTrans       vendTRANS;
        VendSettlement  vendSettlement;
        DialogField     dialogField1;
        Dialog          dialog = new Dialog();

        dialog.caption('Voucher');

        dialogField1 = dialog.addField(ExtendedTypeStr(Name));
        
        dialog.run();

        if (dialog.closedOk())
        {
            select firstonly * from vendTRANS
                where vendTRANS.Voucher == dialogField1.value();
             //  && findCustTRANS.TransType == LedgerTransType::Payment;
            // vendTRANS.AmountMST <= 0 &&
            ////vendTRANS.TransType == LedgerTransType::Vend &&
            // vendTRANS.TransType != LedgerTransType::Settlement &&
            //!vendTRANS.closed

            if (vendTRANS)
            {
                ttsbegin;

                VendTable           VendTable        = VendTable::find(vendTRANS.AccountNum);
                SpecTransManager    specTransManager = SpecTransManager::newRefTableId(VendTable, tableNum(VendSettlement), true);
                boolean 	    isFound = false;
				
                while select forupdate vendSettlement
                    where vendSettlement.TransCompany == vendTRANS.DataAreaId
			&& vendSettlement.TransRecId == vendTRANS.RecId
			&& vendSettlement.AccountNum == vendTRANS.AccountNum
			&& vendSettlement.CanBeReversed == NoYes::Yes
                {
                    vendSettlement.reread();
		    vendSettlement.CustVendSettlement::markThisAndRelatedOffsets(specTransManager, vendTRANS.CurrencyCode);

                    isFound = true;
                }

                if (isFound && VendTrans::reverseTransact(VendTable, null, SettleDatePrinc::DateOfPayment, vendSettlement.TransDate)) 
		{
		    specTransManager.deleteAll();
		}
                ttscommit;
            }
        }
    }
}







Monday, February 6, 2023

Get purch totals amount in d365F&O

 Getting the purch totals amount in d365F&O.

For this I have found the table, which will store the total amount for each purchase order.


  • In purch table form they have added form part.








Table Name : PurchTotalsSummary.


Keep Daxing!!