Thursday, July 20, 2023

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

Generate Sales Packing Slip Report and Email It

Hi All,

In this blog we are going to learn about how to generate a Sales Packing Slip report and send it to the customer's primary email address.

Step 1 Image

Step 1: Create a 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;
    }

   
    /// Export packing slip journal.   
    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: Convert the report byte into a stream and pass it as an attachment to the email

You can get the following 3 parameters like below:

  • CustPackingSlipJour = CustPackingSlipJour::findRecId(_custPackingSlipJourRecId);
  • Email = CustPackingSlipJour.salesTable().customerEmail();
  • 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

Step 4 Image

If condition in computed column.

If Condition in Computed Column

If Condition in Computed Column

Hi all,

Here, we will see how to use the if condition in a computed column. In my requirement, I want to return 0 if RefType is SaftyInvent; otherwise, the value of future days from the same view.

For detailed understanding about computed columns, please check the previous blog.

TestReqTransView is a view created using the ReqTrans table.

X++ Code

private static server str compColumnDelayDays()
{
    DictView reqTransView = new DictView(tableNum(TestReqTransView));
    str reqTransDs = reqTransView.query().dataSourceTable(tableNum(ReqTrans)).name();

    return SysComputedColumn::if(
        SysComputedColumn::equalExpression(
            SysComputedColumn::comparisonField(
                dataentityviewstr(TestReqTransView),
                reqTransDs,
                fieldStr(ReqTrans, Reftype)
            ),
            SysComputedColumn::comparisonLiteral(ReqRefType::SafetyInvent)
        ),
        '0',
        SysComputedColumn::returnField(
            tableStr(TestReqTransView),
            reqTransDs,
            fieldId2name(tableNum(TestReqTransView), fieldNum(TestReqTransView, FUTURESDAYS))
        )
    );
}
    

Mark invent quantity in D365FO using X++

   

Hi All,
Below is the code to mark sales line with correspond Purch line. I used this for completing the marking for migrated purchase line.

X++ Code

 /// <summary>
 /// Mark sales line with purch line
 /// </summary>
 /// <param name = "_issueInventTransId">SalesLine inventrans Recid</param>
 /// <param name = "_receiptInventTransId">PurchLine inventtrans RecId</param>

 public static void completeInventMarking(InventTransId _issueInventTransId, InventTransId _receiptInventTransId)
    {

        InventTransId       issueInventTransId   = _issueInventTransId;
        InventTransId       receiptInventTransId = _receiptInventTransId;
        TmpInventTransMark  tmpInventTransMark;
        Map                 mapUpdated;

        InventTransOriginId receiptInventTransOriginId  =                         InventTransOrigin::findByInventTransId(receiptInventTransId).RecId;

        InventTrans         receiptInventTrans  = InventTrans::findByInventTransOrigin(receiptInventTransOriginId);

         InventTransOriginId issueInventTransOriginId    =   InventTransOrigin::findByInventTransId(issueInventTransId).RecId;

        InventTrans         issueInventTrans            =   InventTrans::findByInventTransOrigin(issueInventTransOriginId);

    InventTransMarkCollection collection = TmpInventTransMark::markingCollection(             InventTransOrigin::find(receiptInventTransOriginId),receiptInventTrans.inventDim(),         receiptInventTrans.Qty);

 collection.insertCollectionToTmpTable(tmpInventTransMark); 

 select firstonly tmpInventTransMark
            where tmpInventTransMark.InventTransOrigin == issueInventTrans.InventTransOrigin                            && tmpInventTransMark.InventDimId       == issueInventTrans.InventDimId;

 if (tmpInventTransMark.RecId != 0)
{
            Qty qtyToMark = issueInventTrans.Qty;

            tmpInventTransMark.QtyMarkNow =  qtyToMark;
            tmpInventTransMark.QtyRemain  -= tmpInventTransMark.QtyMarkNow;

            mapUpdated = new Map(Types::Int64, Types::Record);

            mapUpdated.insert(tmpInventTransMark.RecId, tmpInventTransMark);

             TmpInventTransMark::updateTmpMark(
                        receiptInventTransOriginId,
                        receiptInventTrans.inventDim(),
                        -qtyToMark,
                        mapUpdated.pack());
        }

    }

SalesPackingSlip Report and saving it as a PDF file using x++ code

Calling Sales Packing Slip Report through X++ Code

Hello Blogger Viewers,

Today, I am excited to share with you some X++ code that allows you to call the Sales Packing Slip report programmatically. This can be useful in scenarios where you need to automate the process of generating packing slip reports. Let's dive into the code!

        
SrsReportRunController ssrsController = new SrsReportRunController();
SalesPackingSlipContract salesPackingSlipContract = new SalesPackingSlipContract();
CustPackingSlipJour custPackingSlipJour;
SRSPrintDestinationSettings printerSettings;

select custPackingSlipJour where custPackingSlipJour.PackingSlipId == "200258-PAS-UK01";

// Define which report design to run.
ssrsController.parmReportName(ssrsReportStr(SalesPackingSlip, Report));

// Define how we want to execute the report (right now or batch style).
ssrsController.parmExecutionMode(SysOperationExecutionMode::Synchronous);

// Hide the report dialog.
ssrsController.parmShowDialog(false);

// Set the record and table IDs for the report contract.
salesPackingSlipContract.parmRecordId(custPackingSlipJour.RecId);
salesPackingSlipContract.parmTableId(custPackingSlipJour.TableId);

// Link the contract to the controller so we know how to run the data provider.
ssrsController.parmReportContract().parmRdpContract(salesPackingSlipContract);

// Link the printer settings to the controller.
printerSettings = ssrsController.parmReportContract().parmPrintSettings();

// Set the print medium type to HTML and always overwrite the file if it exists.
printerSettings.printMediumType(SRSPrintMediumType::File);
printerSettings.fileFormat(SRSReportFileFormat::PDF);
printerSettings.overwriteFile(true);
printerSettings.fileName(@'C:\'+'PackingListReport'+custPackingSlipJour.PackingSlipId+'.pdf');

// Start the report execution.
ssrsController.startOperation();
        
    

After executing this code, you will be able to see the generated PDF file in your "Downloads" folder. This approach provides a convenient way to automate the process of generating Sales Packing Slip reports.

I hope you find this code snippet helpful for your X++ development. If you have any questions or suggestions, feel free to leave a comment below.

Happy coding!

How to Post Partial Product Receipt in D365FO Using X++ Code

How to Post Partial Product Receipt in D365FO Using X++ Code How to Post Partial Product Receipt in D365FO Using X++...