Tuesday, February 13, 2024

Post partial packing slip in D365FO

How to Post a Partial Packing Slip in D365FO and AX 2012

How to Post a Partial Packing Slip in D365FO and AX 2012

Understanding the Code

Below is the X++ code snippet demonstrating how to post a partial packing slip for a sales order:

public void postPartialPackingSlip(SalesId _salesId)
{
    SalesFormLetter_PackingSlip     salesFormLetter_PackingSlip;
    salesFormLetter                 salesFormLetter;
    salesFormletterParmData         salesFormLetterParmData;

    SalesParmTable                  salesParmTable;
    SalesParmLine                   salesParmLine;
    salesLine                       salesLine, salesLineUpd;
    salesParmUpdate                 salesParmUpdate;
    SalesTable                      salestable = SalesTable::find(_salesId, true);
    CustPackingSlipJour             custPackingSlipJour;
    TransDate                       packingSlipDate = DateTimeUtil::getSystemDate(DateTimeUtil::getUserPreferredTimeZone());
    InventDim                       inventDim;
    InventLocation                  inventLocation;

    ttsbegin;
    salesFormLetterParmData = salesFormletterParmData::newData(DocumentStatus::PackingSlip, VersioningUpdateType::Initial);

    salesFormLetterParmData.parmOnlyCreateParmUpdate(true);
    salesFormLetterParmData.createData(false);
    salesParmUpdate = salesFormLetterParmData.parmParmUpdate();

    salesParmTable.clear();
    salesParmTable.TransDate                = PackingSlipDate;
    salesParmTable.Ordering                 = DocumentStatus::PackingSlip;
    salesParmTable.ParmJobStatus            = ParmJobStatus::Waiting;
    salesParmTable.salesId                  = salesTable.salesId;

    salesParmTable.salesName                = salesTable.salesName;
    salesParmTable.DeliveryName             = salesTable.DeliveryName;
    salesParmTable.DeliveryPostalAddress    = salesTable.DeliveryPostalAddress;
    salesParmTable.CustAccount              = salesTable.CustAccount;
    salesParmTable.CurrencyCode             = salesTable.CurrencyCode;
    salesParmTable.InvoiceAccount           = salesTable.InvoiceAccount;
    salesParmTable.ParmId                   = salesParmUpdate.ParmId;
    salesParmTable.insert();

    while select salesLine 
        where salesLine.SalesId == salestable.SalesId
            Join InventDim 
            where inventdim.inventDimId == salesLine.InventDimId
                join inventLocation 
                where inventlocation.InventLocationId == inventdim.InventLocationId
                && inventlocation.TestOwnWareHouse == NoYes::Yes
    {
  
        salesParmLine.InitFromsalesLine(salesLine);
        salesParmLine.DeliverNow    = salesLine.SalesQty;
        salesParmLine.ParmId        = salesParmTable.ParmId;
        salesParmLine.TableRefId    = salesParmTable.TableRefId;
        salesParmLine.setQty(DocumentStatus::PackingSlip, false, true);
        salesParmLine.setLineAmount(salesLine);
        salesParmLine.insert();
    }

    salesFormLetter_PackingSlip = salesFormLetter::construct(DocumentStatus::PackingSlip);
    salesFormLetter_PackingSlip.transDate(PackingSlipDate);
    salesFormLetter_PackingSlip.proforma(false);
    salesFormLetter_PackingSlip.specQty(salesUpdate::All);
    salesFormLetter_PackingSlip.salesTable(salesTable);
    salesFormLetter_PackingSlip.parmId(salesParmTable.ParmId);
    salesFormLetter_PackingSlip.salesParmUpdate(salesFormLetterParmData.parmParmUpdate());
    salesFormLetter_PackingSlip.run();
       
    if (salesFormLetter_PackingSlip.parmJournalRecord().TableId == tableNum(custPackingSlipJour))
    {
        custPackingSlipJour = salesFormLetter_PackingSlip.parmJournalRecord();
        info(custPackingSlipJour.packingSlipId)
    }
    ttscommit;
    
}

Steps to Post a Partial Packing Slip

  1. Initialization: The process begins by initializing necessary variables and objects, such as salesFormLetter_PackingSlip and salesParmUpdate.
  2. Creating Sales Parameters: Sales parameters are populated with relevant data from the sales order, such as customer details, delivery information, and currency.
  3. Handling Sales Lines: Iterate through sales lines associated with the sales order. For each line, initialize a salesParmLine object and set relevant parameters. Ensure the line is associated with a warehouse (TestOwnWareHouse == NoYes::Yes) before proceeding.
  4. Generating the Packing Slip: Use the salesFormLetter_PackingSlip object to generate the packing slip. Set parameters such as transaction date and sales table details. Finally, execute the packing slip generation process.
  5. Verifying the Packing Slip: After generating the packing slip, verify if the process was successful by checking the parmJournalRecord. If successful, retrieve relevant information such as the packing slip ID.

Conclusion

Automating the creation and posting of packing slips, including partial packing slips, streamlines the order fulfillment process in D365FO and AX 2012. By leveraging X++ code, businesses can ensure accuracy and efficiency in their shipping operations.

Friday, December 15, 2023

JSON serialization in X++

JSON Serialization in X++

JSON Serialization in X++

TestCustTransExportRequest Class

The TestCustTransExportRequest class represents the request data for exporting customer transactions.

[datacontractAttribute] class TestCustTransExportRequest { String10 dataAreaId; CustAccount custAcc; [DataMemberAttribute("dataAreaId")] public String10 parmDataAreaId(String10 _dataAreaId = dataAreaId) { dataAreaId = _dataAreaId; return dataAreaId; } [DataMemberAttribute("CustomerAccount")] public CustAccount parmCustAcc(CustAccount _custAcc = custAcc) { custAcc = _custAcc; return custAcc; } }

TestCustTransListResponseContract Class

The TestCustTransListResponseContract class is a response data contract representing customer transaction details. This class includes properties for various transaction details such as type, date, amount, and currency. The [DataMemberAttribute] attribute is used to specify the names of these properties in the serialized JSON.

[datacontractAttribute] class TestCustTransListResponseContract { Str60 transType; ExchRate exchRate; String30 transDate; AmountCur amountCur; CurrencyCode currency; TransactionTextLarge description; AmountMST amountMST; AmountCur remainAmountBal; String10 dataAreaId; CustAccount custAcc; [DataMemberAttribute("Channel")] public String10 parmDataAreaId(String10 _dataAreaId = dataAreaId) { dataAreaId = _dataAreaId; return dataAreaId; } [DataMemberAttribute("Customer Id")] public CustAccount parmCustAcc(CustAccount _custAcc = custAcc) { custAcc = _custAcc; return custAcc; } [DataMemberAttribute("Transaction type")] public Str60 parmTransType(Str60 _transType = transType) { transType = _transType; return transType; } [DataMemberAttribute("Date")] public String30 parmTransDate(String30 _transDate = transDate) { transDate = _transDate; return transDate; } [DataMemberAttribute("Description")] public TransactionTextLarge parmDescription(TransactionTextLarge _description = description) { description = _description; return description; } [DataMemberAttribute("Amount in currency")] public AmountCur parmAmountCur(AmountCur _amountMST = amountCur) { amountCur = _amountMST; return amountCur; } [DataMemberAttribute("Currency")] public CurrencyCode parmCurrencyCode(CurrencyCode _currency = currency) { currency = _currency; return currency; } [DataMemberAttribute("Amount in accounting currency")] public AmountMST parmAmountMST(AmountMST _amountMST = amountMST) { amountMST = _amountMST; return amountMST; } [DataMemberAttribute("Exchange rate")] public exchRate parmExchRate(ExchRate _exchRate = exchRate) { exchRate = _exchRate; return exchRate; } [DataMemberAttribute("Balance")] public AmountCur parmRemainAmount(AmountCur _remainAmountBal = remainAmountBal) { remainAmountBal = _remainAmountBal; return remainAmountBal; } }

TestCustTransExportResponse Class

The TestCustTransExportResponse class represents the response data for exporting customer transactions. It contains a list of transaction data. This class is designed to be serialized into a JSON structure containing an array of transactions.

class TestCustTransExportResponse { List transactionData = new List(Types::Class); CustAccount custAcc; String10 dataAreaId; [DataMemberAttribute("Transactions"),DataCollectionAttribute(Types::Class, classStr(TestCustTransListResponseContract))] public List parmTransactionData(List _transactionData = transactionData) { transactionData = _transactionData; return transactionData; } }

TestCustTransExportService Class

The TestCustTransExportService class is a custom web service class that processes export transactions based on the provided request. It uses the other three classes to gather and structure the transaction data, and then returns the response in the specified JSON format.

class TestCustTransExportService extends SysOperationServiceBase { public TestCustTransExportResponse processExportTransaction(TestCustTransExportRequest _requestedCustomer) { TestCustTransExportResponse response = new TestCustTransExportResponse(); CustTrans custTrans; List custTransactionsList = new List(Types::Class); CustAccount custAccount = _requestedCustomer.parmCustAcc(); Microsoft.Dynamics.Ax.Xpp.ErrorException errorEx; try { changecompany(_requestedCustomer.parmDataAreaId()) { while select custTrans where custTrans.AccountNum == custAccount && (custTrans.AmountCur - custTrans.SettleAmountCur) != 0 { TestCustTransListResponseContract custTransListContract = new TestCustTransListResponseContract(); custTransListContract.parmCustAcc(custTrans.AccountNum); custTransListContract.parmDataAreaId(custTrans.DataAreaId); custTransListContract.parmTransType(enum2Str(custTrans.TransType)); custTransListContract.parmTransDate(date2StrUsr(custTrans.TransDate)); custTransListContract.parmDescription(custTrans.Txt); custTransListContract.parmamountCur(custTrans.AmountCur); custTransListContract.parmAmountMST(custTrans.AmountMST); custTransListContract.parmExchRate(custTrans.ExchRate); custTransListContract.parmRemainAmount(custTrans.remainAmountCur()); custTransListContract.parmCurrencyCode(custTrans.CurrencyCode); custTransactionsList.addEnd(custTransListContract); } response.parmTransactionData(custTransactionsList); } } catch(errorEx) { throw error(strFmt("Validation failed for customer %1.",custAccount, errorEx.Message)); } catch (Exception::CLRError) { System.Exception ex = CLRInterop::getLastException(); throw error(strFmt("Validation failed for customer %1.",custAccount, ex.Message)); } return response; } }

JSON Input

{
    "_requestedCustomer": 
    {
        "dataAreaId": "SE02",
        "CustomerAccount": "C0002"
    }
}
        

JSON Output

{
    "$id": "1",
    "Transactions": [
        {
            "$id": "2",
            "Channel": "Site1",
            "Customer Id": "TestCustomer",
            "Transaction type": "Sales order",
            "Date": "11/12/2023",
            "Description": "Credit IN000001VR6 SO SE02-12345VR6 11/12/2023",
            "Amount in currency": -2500.0,
            "Currency": "SEK",
            "Amount in accounting currency": -2500.0,
            "Exchange rate": 100.0,
            "Balance": -2500.0
        },
        {
            "$id": "3",
            "Channel": "Site1",
            "Customer Id": "TestCustomer",
            "Transaction type": "Sales order",
            "Date": "11/12/2023",
            "Description": "Credit IN000001VR7 SO SE02-12345VR7 11/12/2023",
            "Amount in currency": -2500.0,
            "Currency": "SEK",
            "Amount in accounting currency": -2500.0,
            "Exchange rate": 100.0,
            "Balance": -2500.0
        }
    ]
}
        

Wednesday, December 13, 2023

Creating Dependent Tasks in Dynamics 365 Finance and Operations Batch Jobs with X++ Code

Creating Dependent Tasks in Dynamics 365 Finance and Operations Batch Jobs with X++ Code

Creating Dependent Tasks in Dynamics 365 Finance and Operations Batch Jobs with X++ Code

Batch processing is a critical component in handling large-scale data operations within Dynamics 365 Finance and Operations (D365FO). Often, there's a need to create dependent tasks within batch jobs to ensure proper sequencing and execution. In this blog post, we will explore how to create dependent tasks using X++ code in D365FO, based on the provided example.

Understanding the Code

    BatchHeader batchHeader = BatchHeader::construct();

// First task (your first batch job)
DMFBatchImporter batchImporter = new DMFBatchImporter();

batchImporter.batchInfo().parmBatchExecute(NoYes::Yes);
batchImporter.parmReadyForBatchRun(true);
DMFExecutionId batchExecutionId = DMFPackageImporter::PrepareDefinitionGroupForImport(definitionGroupName, executionId, dataAreaId, true);
batchImporter.parmExecutionId(batchExecutionId);

batchHeader.addTask(batchImporter);
batchHeader.addRuntimeTask(batchImporter, BatchHeader::getCurrentBatchTask().RecId);

// Add your second batch job here
TestIntegrationBatch integrationBatch = new TestIntegrationBatch();
integrationBatch.parmLogRecId(logRecId);
integrationBatch.batchInfo().parmGroupId(definition.BatchGroupId);

batchHeader.parmCaption(strFmt('%1 - Test Log - %2', TestIntegrationBatch::description(), logRecId));
// Add second task
batchHeader.addRuntimeTask(integrationBatch, BatchHeader::getCurrentBatchTask().RecId);
// Add dependency
batchHeader.addDependency(integrationBatch, batchImporter, BatchDependencyStatus::FinishedOrError);
batchHeader.save();

  

Step-by-Step Guide on Creating Dependent Tasks

  1. Batch Header Initialization:

    Create a new instance of BatchHeader to represent the batch job.

  2. First Task - DMFBatchImporter:

    Instantiate the primary task (DMFBatchImporter). Set necessary parameters for batch execution. Prepare the execution ID using DMFPackageImporter::PrepareDefinitionGroupForImport. Add the task to the batch header using addTask. Add the task as a runtime task using addRuntimeTask.

  3. Second Task - TestIntegrationBatch:

    Instantiate the secondary task (TestIntegrationBatch). Set any required parameters for this task.

  4. Batch Header Configuration:

    Set the caption for the batch header for better identification. Add the secondary task as a runtime task. Define a dependency between tasks using addDependency. In this case, the dependency is set to FinishedOrError, meaning the second task will execute only when the first task finishes successfully or encounters an error.

  5. Save Batch Header:

    Save the configured batch header to persist the changes.

Conclusion

Creating dependent tasks in batch jobs is essential for maintaining the proper flow of data processing in Dynamics 365 Finance and Operations. The provided X++ code showcases the integration of two tasks with a dependency relationship. Customize this example according to your specific business requirements and extend the logic as needed to handle more complex scenarios in your batch processing workflows.

Wednesday, October 4, 2023

How to send Lasernet report PDF on email.

Calling Lasernet Report from X++ Code in Dynamics 365 Finance and Operations

Calling Lasernet Report from X++ Code in Dynamics 365 Finance and Operations

Published on October 4, 2023 by Your Name

Hi All,

I hope everybody is doing great and learning isn't stopped. Today, I am here with another article that will help us call a Lasernet report from X++ code in Dynamics 365 Finance and Operations. Let me explain my requirement. I have a requirement to send the SalesPackingSlipReport over email, but we don't want to use the standard report designs because we have implemented the ISV solution "Lasernet." So, here comes the challenge: how can we call it? I got some input from the below blog, but after that, I faced lots of problems, but finally, I have achieved it.

Lasernet User Guide

Prerequisites to follow
step 1 :


class FPPrintReport
{
    public static void main(Args _args)
    {
        CustPackingSlipJour custPackingSlipJour = CustPackingSlipJour::findRecId(5637146826);
        FPPrintReport::sendPackingSlipByEmail('200030-SHI-UK01', custPackingSlipJour, 
        	'vijay.yelmame14@gmail.com', 'Test.pdf');
    }

    public static void sendPackingSlipByEmail(WHSShipmentId _shipmentId, 
    	CustPackingSlipJour custPackingSlipJour, Email _ToEmail, Filename _fileName)
    {
        CustFormletterParameters custFormParm = CustFormletterParameters::find();

        if (custFormParm.TestEnableEmailPackSlipReport)
        {
            SalesPackingSlipController formLetterController = SalesPackingSlipController::construct();
            SRSPrintDestinationSettings destinationSettings = new SRSPrintDestinationSettings();
            destinationSettings.printMediumType(SRSPrintMediumType::LAC);

            LACDestinationSettings LACDestinationSettings = new LACDestinationSettings();
            LACDestinationSettings.destTypes4Print(LACDestTypes4Print::Email);
            
            LACDestinationSettings.emailFrom(custFormParm.TestFromEmailAddress);
            LACDestinationSettings.emailTo(_ToEmail);
            LACDestinationSettings.emailSubject(strFmt(custFormParm.TestEmailSubject, _shipmentId));
            LACDestinationSettings.emailBody(custFormParm.TestEmailBody);
            LACDestinationSettings.allowForcedReRun(NoYes::Yes);
            LACDestinationSettings.emailFromName('Test Test');

            destinationSettings.lacDestinationSettings(LACDestinationSettings);

            LACReport report = LACReport::find(ssrsReportStr(Salespackingslip, Report));
            LACResendReport::resendReportFromRecord(report.ReportName, custPackingSlipJour, destinationSettings, true);
        }
    }
}

That's it! You can now follow these steps to call Lasernet reports from your X++ code in Dynamics 365 Finance and Operations. This solution should help you send your SalesPackingSlipReport over email using Lasernet.

If you have any questions or encounter any issues, please feel free to ask. Happy coding!

Monday, August 21, 2023

X++ Code to import file using data entity

internal final class TestDataEntityImport
{
    public static void main(Args _args)
    {
        FileUploadTemporaryStorageResult    fileUpload;
        AsciiStreamIo                       file;
        System.IO.Stream                    stream;

        fileUpload  = File::GetFileFromUser() as FileUploadTemporaryStorageResult;

        file        = AsciiStreamIo::constructForRead(fileUpload.openResult());

        stream      = file.getStream();
        
        TestDataEntityImport  TestDataEntityImport = new TestDataEntityImport();

        TestDataEntityImport.importFromBlob(stream,fileUpload.getFileName());
    }

    public DMFDefinitionGroup findDMFDefinitionGroup(Filename   _fileName)
    {
        DMFDefinitionGroup definitionGroup;
        
        if (strStartsWith(_fileName, 'Test'))
        {
            select firstonly definitionGroup
                where definitionGroup.DefinitionGroupName == 'TestVendorPlantImport'; //DMF import data project
        }

        return definitionGroup;
    }

    public DMFDefinitionGroupEntity findDMFDefinitionGroupEntity(DMFDefinitionGroup _definitionGroup)
    {
        DMFDefinitionGroupEntity definitionGroupEntity;
        DMFEntity dmfEntity;

        select firstonly RecId, Entity from definitionGroupEntity
            exists join dmfEntity
        where definitionGroupEntity.DefinitionGroup == _definitionGroup.DefinitionGroupName
           && dmfEntity.EntityName == definitionGroupEntity.Entity;

        if (!definitionGroupEntity)
        {
            throw error(strFmt("@DMF:DMFNoEntityExists", _definitionGroup.DefinitionGroupName));
        }

        return definitionGroupEntity;
    }

    public DMFLocalFilePath applyTransforms(SharedServiceUnitFileID _uploadedStatement, DMFDefinitionGroup definitionGroup)
    {
        DMFDefinitionGroupEntity    definitionGroupEntity = this.findDMFDefinitionGroupEntity(definitionGroup);
        DMFExecutionId              executionId = DMFUtil::setupNewExecution(definitionGroup.DefinitionGroupName);

        DMFDefinitionGroupExecution execution = DMFDefinitionGroupExecution::find(
            definitionGroup.DefinitionGroupName,
            definitionGroupEntity.Entity,
            executionId,
            true);

        execution.IsTransformed = NoYes::No;
        DMFLocalFilePath filePath = execution.applyTransforms(_uploadedStatement);

        DMFExecution e = DMFExecution::find(executionId, true);
        e.delete();

        return filePath;
    }

    public void importFromBlob(System.IO.Stream  _memory, str _fileName)
    {
        SharedServiceUnitFileID     fileId;
        DMFDefinitionGroup          definitionGroup;
        DMFDefinitionGroupEntity    definitionGroupEntity;
        DMFExecutionId              executionId;
        DMFDefinitionGroupExecution execution;

        //Should be used to get file into FileUploadTemporaryStorageResult object
        FileUploadTemporaryStorageResult result = File::SendFileToTempStore_GetResult(_memory, _fileName);

        if (result && result.getUploadStatus())
        {
            fileId = result.getFileId();

            definitionGroup = this.findDMFDefinitionGroup(_fileName);

            this.applyTransforms(fileId, definitionGroup);

            definitionGroupEntity = this.findDMFDefinitionGroupEntity(definitionGroup);
            executionId = DMFUtil::setupNewExecution(definitionGroup.DefinitionGroupName);

            // Find execution
            execution = DMFDefinitionGroupExecution::find(definitionGroup.DefinitionGroupName, definitionGroupEntity.Entity, executionId, true);
            execution.FilePath = fileId;
            execution.IsTransformed = NoYes::Yes;
            execution.IsSelected = NoYes::Yes;
            execution.ExecuteTargetStep = NoYes::Yes;
            execution.update();

            setPrefix(strFmt("@SYS73667", _fileName));

            // Import the file via quick import DMF
            DMFQuickImportExport::doPGImport(definitionGroup.DefinitionGroupName, executionId, true);

            //deletes file
            result.deleteResult();
        }
    }

}
Importing Files Using Data Management in D365FO/X++

Data migration and integration are critical aspects of any business application, and Microsoft Dynamics 365 Finance and Operations (D365FO) offers a powerful toolset to facilitate these tasks. One of the key features for importing data is the Data Management framework (DMF). In this blog post, we'll explore how to use X++ code to import files using the Data Management framework in D365FO. This code is useful in scenarios where you want to call data entity import throgh integration and also by this code you dont need to worry about how many columns user is going to add later.

Background

The X++ code provided in this blog post demonstrates how to import files using the Data Management framework in D365FO. This code is encapsulated within the TestDataEntityImport class and consists of several methods that work together to achieve a successful file import.


internal final class TestDataEntityImport
{
    public static void main(Args _args)
    {
        FileUploadTemporaryStorageResult fileUpload;
        AsciiStreamIo file;
        System.IO.Stream stream;

        fileUpload = File::GetFileFromUser() as FileUploadTemporaryStorageResult;
        file = AsciiStreamIo::constructForRead(fileUpload.openResult());
        stream = file.getStream();
        
        TestDataEntityImport TestDataEntityImport = new TestDataEntityImport();
        TestDataEntityImport.importFromBlob(stream, fileUpload.getFileName());
    }

    public DMFDefinitionGroup findDefinitionGroup(Filename _fileName)
    {
        DMFDefinitionGroup definitionGroup;
        
        if (strStartsWith(_fileName, 'Test'))
        {
            select firstonly definitionGroup
             where definitionGroup.DefinitionGroupName == 'TestVendorPlantImport'; 
                //DMF import data project
        }

        return definitionGroup;
    }
    public DMFLocalFilePath applyTransforms(SharedServiceUnitFileID _uploadedStatement, DMFDefinitionGroup definitionGroup)
    {
        DMFDefinitionGroupEntity    definitionGroupEntity = this.findDMFDefinitionGroupEntity(definitionGroup);
        DMFExecutionId              executionId = DMFUtil::setupNewExecution(definitionGroup.DefinitionGroupName);

        DMFDefinitionGroupExecution execution = DMFDefinitionGroupExecution::find(
            definitionGroup.DefinitionGroupName,
            definitionGroupEntity.Entity,
            executionId,
            true);

        execution.IsTransformed = NoYes::No;
        DMFLocalFilePath filePath = execution.applyTransforms(_uploadedStatement);

        DMFExecution e = DMFExecution::find(executionId, true);
        e.delete();

        return filePath;
    }

	public void importFromBlob(System.IO.Stream _memory, str _fileName)
    {
        SharedServiceUnitFileID fileId;
        DMFDefinitionGroup definitionGroup;
        DMFDefinitionGroupEntity definitionGroupEntity;
        DMFExecutionId executionId;
        DMFDefinitionGroupExecution execution;

        // Should be used to get file into FileUploadTemporaryStorageResult object
        FileUploadTemporaryStorageResult result 
        	= File::SendFileToTempStore_GetResult(_memory, _fileName);

        if (result && result.getUploadStatus())
        {
            fileId = result.getFileId();
            definitionGroup = this.findDefinitionGroup(_fileName);
            this.applyTransforms(fileId, definitionGroup);

            definitionGroupEntity = this.findDMFDefinitionGroupEntity(definitionGroup);
            executionId = DMFUtil::setupNewExecution(definitionGroup.DefinitionGroupName);

            // Find execution
            execution = DMFDefinitionGroupExecution::find(
                definitionGroup.DefinitionGroupName,
                definitionGroupEntity.Entity,
                executionId,
                true
            );
            
            execution.FilePath = fileId;
            execution.IsTransformed = NoYes::Yes;
            execution.IsSelected = NoYes::Yes;
            execution.ExecuteTargetStep = NoYes::Yes;
            execution.update();

            setPrefix(strFmt("@SYS73667", _fileName));

            // Import the file via quick import DMF
            DMFQuickImportExport::doPGImport(definitionGroup.DefinitionGroupName, executionId, true);

            // Deletes file
            result.deleteResult();
        }
    }
}

Getting Started

The main method serves as the entry point for the file import process. Let's break down the code step by step:

  1. File Upload: The code begins by getting a file from the user using the File::GetFileFromUser() method. The file is uploaded and stored in temporary storage.
  2. File Reading: The uploaded file is then read using an AsciiStreamIo object. This object provides methods to read data from the uploaded file.
  3. Stream Handling: The stream variable is assigned the stream of the file data, which will be used to process the contents of the file.

Conclusion

In this blog post, we've explored how to use X++ code to import files using the Data Management framework in Dynamics 365 Finance and Operations. The provided code demonstrates the step-by-step process of uploading a file, identifying the appropriate DMF Definition Group and Entity, applying transformations, and executing the import.

The Data Management framework in D365FO simplifies data migration and integration tasks, enabling organizations to efficiently import and process large volumes of data. By leveraging the power of X++ and the DMF, businesses can ensure accurate and streamlined data management within their Dynamics 365 environment.

Thursday, August 3, 2023

Attributes cannot be deleted while dependent Provisioned channel product attribute exist. Delete dependent Provisioned channel product attribute and try again.

How to Delete Attributes with Dependent Provisioned Channel Product Attributes in D365 Finance and Operations

Deleting Attributes with Dependent Provisioned Channel Product Attributes

Struggling to remove attributes due to linked Provisioned channel product attributes? Discover a simple solution below!

The Problem:

Trying to delete attributes, but encountering obstacles due to connected Provisioned channel product attributes.

The Solution:

Follow these steps to regain control:

  1. Step 1: Access the Table Browser by adding below line after your dynamics url
    /?mi=SysTableBrowser&prt&limitednav=true&TableName=RETAILPROVISIONEDCHANNELPRODUCTATTRIBUTE&cmp=dat
  2. Step 2: Identify Linked Records
    Locate records connected to the attribute you want to delete. These ties are causing the issue.
  3. Step 3: Delete Dependent Records
    Bid farewell to linked records to break the dependency.

For more insights and learning on #MicrosoftLearn and #D365FO, check out Microsoft Learn's Dynamics 365 Finance and Operations resources.

Thursday, July 20, 2023

How to send SalesPackingSlip report over the email to Customer in D365FO using X++ code

Hi All,
In this blog we are going to learn about how to generate SalesPacking slip report and send it over the customers primary email address.



X++ Code.

Step 1 :Create new class.

internal final class TestPackingSlipJournalExportService
{

    SRSCatalogItemName          reportDesignLocal;
    str                         documentTitle;
    System.IO.MemoryStream      reportMemoryStream;
    SRSPrintMediumType          printMedium;
    LanguageId                  languageId;
    System.Byte[]               reportBytes;

public static TestPackingSlipJournalExportService construct(SRSPrintMediumType _printMedium)
{

        TestPackingSlipJournalExportService    service = new TestPackingSlipJournalExportService();

        service.parmPrintMedium(_printMedium);

        return service;

}

public SRSPrintMediumType parmPrintMedium(SRSPrintMediumType _printMedium = printMedium){

        printMedium = _printMedium;
        return printMedium;
 }

   /// <summary>
    /// Export packing slip journal.
    /// </summary>
    /// <param name = "_custPackingSlipJourRecId"></param>
    /// <param name = "_fileName">File name</param>
    /// <returns>Report in byte</returns>

public System.Byte[] exportPackingSlipJournal(RecId _custPackingSlipJourRecId, str _fileName)
{

        SalesPackingSlipContract    salesPackingSlipContract;
        SalesPackingSlipController  formLetterController        = SalesPackingSlipController::construct();        CustPackingSlipJour         custPackingSlipJour  = CustPackingSlipJour::findRecId(_custPackingSlipJourRecId);

        Common                      printMgmtReferencedTable;
        FormLetterReport            formLetterReport;
        PrintMgmtPrintSettingDetail printSettingDetail;
        SalesTable                  salesTable = custPackingSlipJour.salesTable();

        PrintMgmtReportFormatName   printMgmtReportFormatName   = PrintMgmtDocType::construct(PrintMgmtDocumentType::SalesOrderPackingSlip).getDefaultReportFormat();

        reportDesignLocal = printMgmtReportFormatName;

     if (!reportDesignLocal)
    {
            reportDesignLocal = ssrsReportStr(SalesPackingSlip, Report)
     }

      formLetterReport = FormLetterReport::construct(PrintMgmtDocumentType::SalesOrderPackingSlip);

        // Determine where to start looking for Print Mgmt settings
        printMgmtReferencedTable = salesTable;

        languageId = salesTable.LanguageId ? salesTable.LanguageId : custPackingSlipJour.LanguageId;

        formLetterReport.loadPrintSettings(custPackingSlipJour, printMgmtReferencedTable, custPackingSlipJour.LanguageId);

         if (formLetterReport.moveNextPrintSetting())
        {

            printSettingDetail = formLetterReport.getCurrentPrintSetting();

            formLetterReport.parmReportRun().loadSettingDetail(printSettingDetail);

        }

        formLetterReport.parmPrintType(PrintCopyOriginal::Original);
       salesPackingSlipContract = new SalesPackingSlipContract();


        salesPackingSlipContract.parmRecordId(custPackingSlipJour.RecId);
        salesPackingSlipContract.parmTableId(custPackingSlipJour.TableId);

        salesPackingSlipContract.parmDocumentTitle("@ExtendedItallianLocalization:DeliveryNote");

        this.generateReport(salesPackingSlipContract, _fileName);

        return this.getReportBytes();

    }

    private void generateReport(Object _rdpContract, str _fileName)
    {

        SrsReportRunController  srsReportRunController = new SrsReportRunController();
        srsReportRunController.parmReportName(reportDesignLocal);
        srsReportRunController.parmExecutionMode(SysOperationExecutionMode::Synchronous);
        srsReportRunController.parmShowDialog(false);
        srsReportRunController.parmReportContract().parmRdpContract(_rdpContract);

        if (languageId)
        {            srsReportRunController.parmReportContract().parmRdlContract().parmLanguageId(languageId);            srsReportRunController.parmReportContract().parmRdlContract().parmLabelLanguageId(languageId);       }

        srsReportRunController.parmReportContract().parmReportExecutionInfo(new SRSReportExecutionInfo());
       srsReportRunController.parmReportContract().parmReportServerConfig(SRSConfiguration::getDefaultServerConfiguration());

        SRSPrintDestinationSettings printerSettings = srsReportRunController.parmReportContract().parmPrintSettings();

        printerSettings.printMediumType(printMedium);
        printerSettings.fileFormat(SRSReportFileFormat::PDF);
        printerSettings.parmFileName(_fileName);
        printerSettings.overwriteFile(true);
        SRSReportRunService srsReportRunService = new SrsReportRunService();

       srsReportRunService.getReportDataContract(srsReportRunController.parmReportContract().parmReportName());

     srsReportRunService.preRunReport(srsReportRunController.parmReportContract());

        Map reportParametersMap = srsReportRunService.createParamMapFromContract(srsReportRunController.parmReportContract());

        Microsoft.Dynamics.AX.Framework.Reporting.Shared.ReportingService.ParameterValue[]  parameterValueArray = SrsReportRunUtil::getParameterValueArray(reportParametersMap);

        SRSProxy srsProxy = SRSProxy::constructWithConfiguration(srsReportRunController.parmReportContract().parmReportServerConfig());

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

    public System.Byte[] getReportBytes()
    {
        return reportBytes;
    }

}


Step 2: Call the above created class and get the report bytes.

Step 3: Covert the report byte into stream and pass it as attachment to email.

You can get below 3 parameters like below.

1)CustPackingSlipJour = CustPackingSlipJour::findRecId(_custPackingSlipJourRecId);
2)Email = CustPackingSlipJour.salesTable().customerEmail();
3)FileName = "Test.pdf"

public static void sendPackingSlipByEmail(CustPackingSlipJour _custPackingSlipJour, Email _ToEmail, Filename _fileName)
{
       TestPackingSlipJournalExportService service = TestPackingSlipJournalExportService::construct(SRSPrintMediumType::File);

      System.Byte[] reportBytes = service.exportPackingSlipJournal(_custPackingSlipJour.RecId,_fileName);

        if (reportBytes)
        {

            SysMailerMessageBuilder messageBuilder = new SysMailerMessageBuilder();

            try
            {
                System.IO.Stream stream =   new System.IO.MemoryStream(reportBytes);

                stream.Position = 0;
                messageBuilder.setBody("Packing Slip id - " + _custPackingSlipJour.PackingSlipId);
                messageBuilder.setSubject("Packing slip journal confirmation");
                messageBuilder.addTo(_ToEmail);
                messageBuilder.addAttachment(stream, _filename);
                messageBuilder.setFrom('no-reply@Test.com');

               SysMailerFactory::sendNonInteractive(messageBuilder.getMessage());

            }
            catch
            {
                exceptionTextFallThrough();
            }

        }

    }

Step 4: Result in email



Post partial packing slip in D365FO

How to Post a Partial Packing Slip in D365FO and AX 2012 How to Post a Partial Packing Slip in D365FO and AX 2012 Understanding t...