Saturday, December 21, 2024

D365 Sending Email with Docentric Template for SalesInvoice.Report report as attachment using X++

 

Docentric for Dynamics 365 FO


public final class  SendEmailHelper

{

    public static str  CCEmail, Esender;

    public static LogisticsElectronicAddressLocator GetPrimaryEmail(AccountNum ACCNum)

    {

        if (!ACCNum)

        {

            throw error("Account number cannot be null or empty.");

        }


        DirPartyTable dirPartyTable = DirPartyTable::findRec(CustTable::find(ACCNum).Party);

        if (!dirPartyTable)

        {

            throw error("No party found for the provided account number.");

        }


        LogisticsElectronicAddress logisticsElectronicAddress;

        DirPartyLocation dirLocation;


        select logisticsElectronicAddress

        join dirLocation

        where dirLocation.Location == logisticsElectronicAddress.Location &&

              logisticsElectronicAddress.IsPrimary &&

              dirLocation.Party == dirPartyTable.RecId &&

              logisticsElectronicAddress.Type == LogisticsElectronicAddressMethodType::Email;


        if (!logisticsElectronicAddress)

        {

            warning("No primary email address found.");

        }


        return logisticsElectronicAddress.Locator;

    }


    public static container getEmailTemplate(SysEmailId _emailId, LanguageId _languageId)

    {

        // Info(_emailId);


        SysEmailMessageSYSTEMTable  messageTable    = SysEmailMessageSYSTEMTable::find(_emailId, _languageId);

        SysEmailSYSTEMTable         emailTable      = SysEmailSYSTEMTable::find(_emailId);


        if (!messageTable && emailTable)

        {

            // Try to find the email message using the default language from the email parameters

            messageTable = SysEmailMessageSYSTEMTable::find(_emailId, emailTable.DefaultLanguage);

            

        }


        if (messageTable)

        {

            //CCEmail = emailTable.EmailId;

            return [messageTable.Subject, messageTable.Mail, emailTable.SenderAddr, emailTable.EmailId];

        }

        else

        {

            warning("We didn't find a template"); // Let the user know we didn't find a template

            return ['', '', emailTable.SenderAddr, emailTable.SenderName, emailTable.EmailId];

        }

    }


    public static str Email_Body(CustInvoiceJour _custinvoicejour)

    {

        str InvoiceId = _custinvoicejour.InvoiceId;

        str salesid = _custinvoicejour.SalesId;

        str invoiceDate = date2str(DateTimeUtil::date(_custinvoicejour.InvoiceDate), 123,DateDay::Digits2,DateSeparator::Slash,DateMonth::Digits2,DateSeparator::Slash,DateYear::Digits4);

        str customerName = CustTable::find(_custinvoicejour.InvoiceAccount).name();

        str orderDate = date2str(DateTimeUtil::date(SalesTable::find(_custinvoicejour.SalesId).CreatedDateTime), 123,DateDay::Digits2,DateSeparator::Slash,DateMonth::Digits2,DateSeparator::Slash,DateYear::Digits4);

        //str trackingNumber 

        RetailConfigurationParameters _RetailConfigurationParameters = RetailConfigurationParameters::findByName("INVOICED_TEMPLATE_ID");

        str sub,tmp,Sender,frm;

        str EmailId = _RetailConfigurationParameters.Value;

          


        [sub,tmp,Sender,frm] =   SendEmailHelper::getEmailTemplate(EmailId,'en-us');

        Esender  = sender;


        tmp = strReplace(tmp,'%salesid%','%1');

        tmp = strReplace(tmp,'%invoiceId%','%2');

        tmp = strReplace(tmp,'%invoiceDate%','%3');

        tmp = strReplace(tmp,'%customername%','%4');

        tmp = strReplace(tmp,'%orderDate%','%5');

        str ret = strFmt(tmp,salesid,InvoiceId,invoiceDate,customerName ,orderDate);


        return ret;

    }


    public static void Sendmail(CustInvoiceJour _custinvoicejour)

    {

        System.Exception ex;

        try

        {

            if (!_custinvoicejour)

            {

                throw error("CustInvoiceJour parameter is null.");

            }


            str invoiceId = _custinvoicejour.InvoiceId;

            str body = SendEmailHelper::Email_Body(_custinvoicejour);

            str emailTo = SendEmailHelper::GetPrimaryEmail(_custinvoicejour.InvoiceAccount);


            if (!emailTo)

            {

                throw error("No email address found for the customer.");

            }


            SysMailerMessageBuilder builder = new SysMailerMessageBuilder();

            builder.setBody(body);

            builder.setFrom(Esender);

            builder.addTo(emailTo);

            builder.setSubject("Your order has been invoiced - " + invoiceId);


            System.IO.Stream stream = SendEmailHelper::generateReportStream(_custinvoicejour.RecId);

            if (stream)

            {

                builder.addAttachment(stream, "Your order has been invoiced - " + invoiceId + ".pdf");

            }

            else

            {

                warning("Stream for the report attachment could not be generated.");

            }


            SysMailerFactory::getNonInteractiveMailer().sendNonInteractive(builder.getMessage());

        }

        catch (Exception::CLRError)

        {

            ex = CLRInterop::getLastException();

            throw error(ex.Message);

        }

    }


    public static System.IO.MemoryStream generateReportStream(RecId invJournalRecId)

    {

        SalesInvoiceContract    contract = new SalesInvoiceContract();

        DocReportTemplate _DocReportTemplate ;


        //Get Default Template to use from Docentric

        select firstonly _DocReportTemplate 

            where _DocReportTemplate.Report == DOCREPORTTABLE::findReportId("SalesInvoice.Report").RecId

            && _DocReportTemplate.IsDefaultTemplate == NoYes::Yes;


        //Get Invoice Record

        CustInvoiceJour CustInvoiceJour = CustInvoiceJour::findRecId(invJournalRecId);

        Filename fileName = _DocReportTemplate.TemplateId;  


        // Initialize the SalesInvoiceController

        SrsReportRunController controller = new SrsReportRunController();

        Args args = new Args();


        // Attach the CustInvoiceJour record to the arguments

        args.record(CustInvoiceJour);

        controller.parmArgs(args);


        // Set the report name

        controller.parmReportName(ssrsReportStr(SalesInvoice, Report));     

        contract.parmRecordId(CustInvoiceJour.RecId);

        controller.parmShowDialog(false);

        controller.parmReportContract().parmRdpContract(contract);


        // Use Docentric to generate the report

        DocSrsReportGenerator docReportGenerator = new DocSrsReportGenerator(controller);


        // Configure Docentric output settings directly in the generator

        docReportGenerator.setPrintDestinationSettings_DocentricReport(DocOutputFileFormat::PDF, fileName);


        // Generate the report as a container

        container reportContainer = docReportGenerator.generateReport();


        // Convert container to MemoryStream

        System.IO.MemoryStream reportStream;

        if (reportContainer)

        {

            reportStream = DocGlobalHelper::convertContainerToMemoryStream(reportContainer);

        }


        return reportStream;

    }


}

Wednesday, July 24, 2024

D365 Sending Email with Customer Account Statement SSRS report as attachment using X++

D365 Sending Email with Customer Account Statement SSRS report as attachment using X++




 custTable _custTable;
              

                SysOperationQueryDataContractInfo sysOperationQueryDataContractInfo;
                SrsReportRunController reportRunController;
                CustTransListContract custTransListContract;
                SRSReportExecutionInfo reportExecutionInfo;
                SRSPrintDestinationSettings printDestinationSettings;
                SRSReportRunService srsReportRunService;
                SRSProxy srsProxy;
                QueryBuildRange qbrCustAccount;
                QueryBuildDataSource queryBuildDataSource;
                Object dataContractInfoObject;
                Map reportParametersMap;
                Map mapCustAccount;
                MapEnumerator mapEnumerator;
                Array arrayFiles;
                System.Byte[] reportBytes;
                Filename fileName;
                Args args;
                System.IO.MemoryStream memoryStream;
                System.IO.MemoryStream fileStream;
                CustParameters custParameters;
                Email toEmail;


                Map                                 templateTokens;
                str                                 emailSenderName;
                str                                 emailSenderAddr;
                str                                 emailSubject;
                str                                 emailBody;
 
                Microsoft.Dynamics.AX.Framework.Reporting.Shared.ReportingService.ParameterValue[]  parameterValueArray;
 
                #define.Subject("Subject")
                #define.CustAccount("CustAccount")
                #define.EmailDate("Date");
 
                custParameters          = CustParameters::find();
 
                reportRunController     = new SrsReportRunController();
                custTransListContract   = new CustTransListContract();
                reportExecutionInfo     = new SRSReportExecutionInfo();
                srsReportRunService     = new SrsReportRunService();
                reportBytes             = new System.Byte[0]();
                args                    = new Args();
                templateTokens          = new Map(Types::String, Types::String);
                var messageBuilder      = new SysMailerMessageBuilder();
 
                // custTransListContract.parmNewPage(NoYes::Yes);
                str custAccount = "10000";
                _custTable = CustTable::find(custAccount);
                if(!_custTable)
                {
                    return "No Customer found";
                }
                if(!emailAddress)
                {
                    return "No emailAddress";
                }
                fileName    = strFmt("CustomerAccountStatement_%1.pdf", _custTable.AccountNum);
 
                reportRunController.parmArgs(args);
                reportRunController.parmReportName(ssrsReportStr(CustTransList, Report));
                reportRunController.parmShowDialog(false);
                reportRunController.parmLoadFromSysLastValue(false);
                reportRunController.parmReportContract().parmRdpContract(custTransListContract);
 
                // Modify query
                mapCustAccount = reportRunController.getDataContractInfoObjects();
                mapEnumerator = mapCustAccount.getEnumerator();
 
                while (mapEnumerator.moveNext())
                {
                    dataContractInfoObject = mapEnumerator.currentValue();
 
                    if (dataContractInfoObject is SysOperationQueryDataContractInfo)
                    {
                        sysOperationQueryDataContractInfo = dataContractInfoObject;
 
                        queryBuildDataSource    = SysQuery::findOrCreateDataSource(sysOperationQueryDataContractInfo.parmQuery()
                                                                    , tableNum(CustTable));
                        qbrCustAccount          = SysQuery::findOrCreateRange(queryBuildDataSource, fieldNum(CustTable, AccountNum));
                        qbrCustAccount.value(_custTable.AccountNum);
                    }
                }
 
                printDestinationSettings = reportRunController.parmReportContract().parmPrintSettings();
                printDestinationSettings.printMediumType(SRSPrintMediumType::File);
                printDestinationSettings.fileName(fileName);
                printDestinationSettings.fileFormat(SRSReportFileFormat::PDF);
 
                reportRunController.parmReportContract().parmReportServerConfig(SRSConfiguration::getDefaultServerConfiguration());
                reportRunController.parmReportContract().parmReportExecutionInfo(reportExecutionInfo);
 
                srsReportRunService.getReportDataContract(reportRunController.parmreportcontract().parmReportName());
                srsReportRunService.preRunReport(reportRunController.parmreportcontract());
 
                reportParametersMap = srsReportRunService.createParamMapFromContract(reportRunController.parmReportContract());
                parameterValueArray = SrsReportRunUtil::getParameterValueArray(reportParametersMap);
 
                srsProxy        = SRSProxy::constructWithConfiguration(reportRunController.parmReportContract().parmReportServerConfig());
                reportBytes     = srsproxy.renderReportToByteArray(reportRunController.parmreportcontract().parmreportpath()
                                                    , parameterValueArray
                                                    , printDestinationSettings.fileFormat()
                                                    , printDestinationSettings.deviceinfo());
 
                memoryStream    = new System.IO.MemoryStream(reportBytes);
                memoryStream.Position = 0;
 
                fileStream      = memoryStream;
                toEmail         =  this.getCustEmail(_custTable.AccountNum);
 
      

                    emailBody = "Dear Customr, Please find attached Customer account statement till today.";
                    emailSubject = strFmt("Customer account statement for %1", _custTable.AccountNum);
                    emailSenderAddr = "msd@test.com";
                    emailSenderName = strFmt("Customer account statement for %1", _custTable.AccountNum);


                    messageBuilder.addTo(toEmail)
                        .setSubject(emailSubject)
                        .setBody(SysEmailMessage::stringExpand(emailBody, SysEmailTable::htmlEncodeParameters(templateTokens))).addCC("");
 
                 
                    messageBuilder.setFrom(emailSenderAddr , emailSenderName );
           
                    messageBuilder.addAttachment(fileStream, fileName);
 
                    SysMailerFactory::sendNonInteractive(messageBuilder.getMessage());



public Email getCustEmail(CustAccount _custAccount)
{
    CustTable                   custTable;
    DirPartyLocation            dirPartyLocation;
    LogisticsLocation           logisticsLocation;
    LogisticsElectronicAddress  logisticsElectronicAddress;
 
    custTable = CustTable::find(_custAccount);
 
    select firstonly Location, Party from dirPartyLocation
        where dirPartyLocation.Party                        == custTable.Party
            join RecId from logisticsLocation
                where logisticsLocation.RecId               == dirPartyLocation.Location
            join Locator from logisticsElectronicAddress
                where logisticsElectronicAddress.Location   == logisticsLocation.RecId
                    && logisticsElectronicAddress.Type      == LogisticsElectronicAddressMethodType::Email
                    && logisticsElectronicAddress.IsPrimary == NoYes::Yes;
 
    return logisticsElectronicAddress.Locator;
}

Tuesday, July 23, 2024

clicking link on info message X++ to Open form

 Message::AddAction() method can be used to embed an action within a message sent to the message bar. This method supports adding a single action that is associated with a display or action menu item, which is then visualized as a link button, redirecting users directly to the form with the correct record selected. Here's how it can be used.



public static void pushNotification(str  NotificationMsg, str actionText,LedgerJournalTable journalTable)
{
    MenuItemMessageAction actionData = new MenuItemMessageAction();
    actionData.MenuItemName(menuItemDisplayStr(LedgerJournalTable));
    actionData.TableName(tableStr(LedgerJournalTable));
    actionData.RecId(journalTable.RecId);
    
    str jsonData = FormJsonSerializer::serializeClass(actionData);
 
    int64 messageId = Message::AddAction(MessageSeverity::Informational,NotificationMsg ,actionText, MessageActionType::DisplayMenuItem, jsonData);
} 

Muhammad Aamir Hanif Reference 


Thursday, November 30, 2023

D365 FO - Send Email with attachments by X++

 

Go to System administration >> Setup >> Email >> System email templates

Create New template 


[ExtensionOf(classStr(PurchConfirmationJournalPost))]
final class PurchConfirmationJournalPost_Extension
{
    public str  CCEmail, Esender;

    public void Sendmail()
    {
        try
        {
            str vendname = purchTable::find(this.vendPurchOrderJour.purchid).vendorName(); 
            str Purchid = this.vendPurchOrderJour.purchid;
            str sUserId;
             
                sUserId = curUserId();
                //Getting Email id from User Options --> Account --> Email Provider --> Email id
                str userEmail = SysUserInfo::find(sUserId, false).Email;
            
                var builder = new SysMailerMessageBuilder();
                builder.setBody(this.Email_Body());
                builder.setFrom(userEmail,Esender);
                builder.addcc(ccEmail);
               
                builder.addTo("test@test.com");
                builder.setSubject("subject string");
            
                //************
                System.IO.Stream     stream = this.generateReportStream(); // todo insert record reference
                if(stream)
                    builder.addAttachment(stream , vendname +" - "+Purchid+".pdf");
                var message = builder.getMessage();
               
              SysMailerFactory::getNonInteractiveMailer().sendNonInteractive(message);
            
        }
        catch (Exception::Error)
        {
            throw error("@SYS33567");
        }
    }


  public str Email_Body()
    {
        str vendname = purchTable::find(this.vendPurchOrderJour.purchid).vendorName();
        str Purchid = this.vendPurchOrderJour.purchid;

        str sub,tmp,Sender,frm,cc;
        [sub,tmp,Sender,frm,cc] =   this.getEmailTemplate('POConfirm','en-us');
        Esender  = sender;
        tmp = strReplace(tmp,'%vend%','%1');
        tmp = strReplace(tmp,'%purchid%','%2');
        str ret = strFmt(tmp,vendname,Purchid);

        return ret;
    }

 public  container getEmailTemplate(SysEmailId _emailId, LanguageId _languageId)
    {
        // Info(_emailId);

        SysEmailMessageSYSTEMTable  messageTable    = SysEmailMessageSYSTEMTable::find(_emailId, _languageId);
        SysEmailSYSTEMTable         emailTable      = SysEmailSYSTEMTable::find(_emailId);
      
        //  Info(emailTable.DefaultLanguage);

        if (!messageTable && emailTable)
        {
            // Try to find the email message using the default language from the email parameters
            messageTable = SysEmailMessageSYSTEMTable::find(_emailId, emailTable.DefaultLanguage);
            
        }

        if (messageTable)
        {
          //  info(emailTable.CCEmail);
            CCEmail = emailTable.CCEmail;
            return [messageTable.Subject, messageTable.Mail, emailTable.SenderAddr, emailTable.CCEmail];
        }
        else
        {
            warning("We didn't find a template"); // Let the user know we didn't find a template
            return ['', '', emailTable.SenderAddr, emailTable.SenderName, emailTable.ccemail];
        }
    }
 public System.IO.MemoryStream generateReportStream()
    {
        DocuRef                         addedRecord;
        VendPurchOrderJour              VendPurchOrderJour = this.vendPurchOrderJour;
        Filename                        fileName = "Report.pdf";
        PurchPurchaseOrderController controller = new PurchPurchaseOrderController();
        PurchPurchaseOrderContract    contract = new PurchPurchaseOrderContract();
        SRSPrintDestinationSettings     settings;
        Array                           arrayFiles;
        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();
        System.IO.MemoryStream          stream;
        Args                            args = new Args();       ;

        args.record(VendPurchOrderJour);
        contract.parmRecordId(VendPurchOrderJour.RecId);
        controller.parmArgs(args);
        controller.parmReportName(ssrsReportStr(PurchPurchaseOrder, Report));
        controller.parmShowDialog(false);
        controller.parmReportContract().parmRdpContract(contract);
        settings = controller.parmReportContract().parmPrintSettings();
        settings.printMediumType(SRSPrintMediumType::File);
        settings.fileName(fileName);
        settings.fileFormat(SRSReportFileFormat::PDF);
        controller.parmReportContract().parmReportServerConfig(SRSConfiguration::getDefaultServerConfiguration());
        controller.parmReportContract().parmReportExecutionInfo(executionInfo);
        srsReportRunService.getReportDataContract(controller.parmreportcontract().parmReportName());
        srsReportRunService.preRunReport(controller.parmreportcontract());
        reportParametersMap = srsReportRunService.createParamMapFromContract(controller.parmReportContract());
        parameterValueArray = SrsReportRunUtil::getParameterValueArray(reportParametersMap);
        srsProxy = SRSProxy::constructWithConfiguration(controller.parmReportContract().parmReportServerConfig());
        reportBytes = srsproxy.renderReportToByteArray(controller.parmreportcontract().parmreportpath(),
                                              parameterValueArray,
                                              settings.fileFormat(),
                                              settings.deviceinfo());

        if (reportBytes)
        {
            stream = new System.IO.MemoryStream(reportBytes);
        }
        return stream;
    }
  }


Monday, November 27, 2023

D365 FO Data entity Export using X++

 

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");
        }

    }
}

References   Ref  Ref2

D365 FO Encryption And Decryption Using A Symmetric Key(AES) using X++

 

Encryption And Decryption Using A Symmetric Key(AES) using x++

 Encryption And Decryption Using A Symmetric Key(AES) using x++.


I received a requirement to generate an XML file with encrypted data using a symmetric key. The recipients on the other side will decrypt the text using the same symmetric key. To test this, I used a dummy value such as 'RRR'.

To achieve this, I wrote the code in C# and added the resulting DLL to my project references.


C# Code:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace EncryptionDecryptionUsingSymmetricKey
{
    public class AesOperation
    {
        public static string EncryptString(string key, string plainText)
        {
            byte[] iv = new byte[16];
            byte[] array;

            using (Aes aes = Aes.Create())
            {
                aes.Key = Encoding.UTF8.GetBytes(key);
                aes.IV = iv;

                ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

                using (MemoryStream memoryStream = new MemoryStream())
                {
                    using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter streamWriter = new StreamWriter((Stream)cryptoStream))
                        {
                            streamWriter.Write(plainText);
                        }

                        array = memoryStream.ToArray();
                    }
                }
            }

            return Convert.ToBase64String(array);// It will convert bytes to string
        }

        public static string DecryptString(string key, string cipherText)
        {
            byte[] iv = new byte[16];
            byte[] buffer = Convert.FromBase64String(cipherText); // It will convert string to bytes
using (Aes aes = Aes.Create()) { aes.Key = Encoding.UTF8.GetBytes(key); aes.IV = iv; ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV); using (MemoryStream memoryStream = new MemoryStream(buffer)) { using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read)) { using (StreamReader streamReader = new StreamReader((Stream)cryptoStream)) { return streamReader.ReadToEnd(); } } } } } } }



X++ Code:

b14ca5898a4e4133bbce2ea2315a1916

Using EncryptionDecryptionUsingSymmetricKey;

internal final class TestRunnableClass1
{
    public static void main(Args _args)
    {
        str key = "b65ff7654brt8799fghj4ed7892b6798";
 
        str	text = 'RRR';
 
        str encryptedString = AesOperation::EncryptString(key, text);
 
        info(encryptedString);
 
        str decryptedString = AesOperation::DecryptString(key, encryptedString);
 
        info(decryptedString);
    }
}
References   Ref1  Ref2 


Get multiple selected rows using D365 X++

 

First You need to insure that Multi Select for Button is True 





Create your extension class and add follow code below 


  [FormControlEventHandler(formControlStr(CustFreeInvoice, MultipleSelect), FormControlEventType::Clicked)]
    public static void  MultipleSelect_OnClicked(FormControl sender, FormControlEventArgs e)
    {
        MultiSelectionHelper          selectionHelper = MultiSelectionHelper::construct();
        Set                           selectedRecords = new Set(Types::Record);
        FormRun formrun                         = sender.formRun();
        CustInvoiceTable _CustInvoiceTable   = formrun.dataSource().cursor();
        CustInvoiceTable                       myTable;
     //   super();
    
        FormDataSource     _CustInvoiceTable_ds = sender.formRun().dataSource(formDataSourceStr(CustFreeInvoice,CustInvoiceTable)) as FormDataSource;
      
        selectionHelper.parmDataSource(_CustInvoiceTable_ds);
        myTable  = selectionHelper.getFirst();
 
        if (myTable.RecId)
        {
            while (myTable)
            {
                selectedRecords.add(myTable);
                info(strFmt("Selected record is %1",myTable.RecId));//Display selected record
                myTable = selectionHelper.getNext();
            }
        }
    }