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_TraxSCM_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 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();
            }
        }
    }





Monday, November 20, 2023

D365 Solution for Cloud Dev machine show error ' The remote certificate is invalid according to the validation procedure. '

 

You are getting 'The remote certificate is invalid according to the validation procedure.'Error show with all reports and  tired Rotate SSL Certificate but not complete.




will require applying the below steps and if still the same issue after this so you will need to redeploy this environment.
  1. Navigate to Lifecycle Services.

  2. In the Shared Asset library, click the Model

  3. Download the Renew WinRM certificate folder

  4. RDP to the environment

  5. Extract the zip file to a local folder

  6. Open mmc.exe and add Certificate snap-in (Computer)

  7. Open Personal\Certificates and locate the certificate with your VMs name which should have the expiration date passed

  8. Browse to the RenewWinRMCertificate folder that was previously created from extracting the zip file

  9. Select File > Open Windows PowerShell with elevated privileges (Run as Administrator)

  10. Run .\VirtualMachine-RegenerateWinRMCertificate.ps1 from the folder

  11. Refresh the Certificates console and confirm the Certificate has been created and the expiration date is now valid. (Optional: Delete expired certificate to avoid confusion)

  12. Restart the Environment from LCS (Stop \ Start)

  13. In LCS select Maintain > Rotate secrets

  14. Select 'Rotate the SSL certificates'

How to update the WinRM SSL certificate on environments deployed in your subscription - Microsoft Dynamics 365 Blog

Rotate the expired or nearly expired SSL certificate on your subscription's one-box environments - Microsoft Dynamics 365 Blog



in case when running step number 10  (Run .\VirtualMachine-RegenerateWinRMCertificate.ps1 from the folder ) and show below error 


1: Configure Network Security Rules>Go to Network Security Group of the Azure VM and change 'powershell-remote-rule' from Deny to Allow (this is just temporary; we will change it back after completing the rotation). 

Then run script It should work fine.




Thursday, June 22, 2023

How to get sales tax and Purch tax percentage X++

private TaxValue getTaxPercent(TaxGroup _taxGroup, TaxItemGroup _taxItemGroup)

{

    TaxGroupData    taxGroupData ;

    TaxOnItem       taxOnItem;

    TaxData         taxData;


    select firstonly TaxCode from taxGroupData

        index hint TaxGroupIdx

            where taxGroupData.TaxGroup == _taxGroup

            join taxOnItem

            where taxOnItem.TaxItemGroup == _taxItemGroup &&

                    taxOnItem.TaxCode == taxGroupData.TaxCode;


    select firstonly TaxValue from taxData where taxData.TaxCode == taxGroupData.TaxCode;

    return taxData.TaxValue;


ref

}

Wednesday, May 3, 2023

D365 F&O setup email notifications on workflows

 

How to setup email notifications on workflows in D365 F&O


Today we’re going to see how to manage workflow notifications in D365 Supply Chain Management.

Indeed, there are 3 methods that we will discover during 3 chapters: Via the calendar, via the action center or via e-mails.

Original post Reference

Via the calendar

The first one is the basic method, which is visible directly on the home screen.

The only condition is that you must have launched the workflow batch in « System administration \ WorkFlows \ Workflow infrastructure configuration » :



There are some HyperText links under the calendar :



Via Action Center

The second one is via the action center.

For this method a little more configuration is necessary but nothing too complicated.

Indeed in the options of the user it is necessary to go to check the box « Send notifications to Action Center » :



This has the effect of adding this type of notification:



and if we open the pane on the right we have more detail :



Note that there is sometimes a delay before the appearance of the notification.

Via e-mail

The last method is to send email notifications.

And here … it’s not the same story anymore.

3 methods of sending emails are configurable : EML, Exchange et SMTP.

Here we will focus on the SMTP method.

Let’s start by looking at what to do with user options.

there is two things to do :

·        Configure the email address that will receive the notifications. If this field is empty, notifications will not be sent:



·        Check the case « Send notifications in email » :



This is all we have to do at the user level (except that it must be done for all users who want to receive email notifications).

Now let’s go to the system settings.

The first step is to configure the sending of emails for SMTP.

To do this, go to the module « Systeme Administration \ Setup \ E-mail \ E-mail  Setup» :



In the configuration section, activate the SMTP service, set SMTP as the default provider of batch messaging and optionally limit the size of attachments.



In the « SMTP Parameter » section, contact your favorite IT specialist to obtain the server address, port, user name and password required for sending :



Once the configuration is done, it is always useful to send a test email to validate that everything is good.

The second step is to start a batch process which will take care of sending emails.

For that, go to the module « System Administration \ Periodic tasks \ Email processing \ Email distributor batch » :



Configure a frequency according to the needs of the company (every hour, 10 minutes, etc.).

We come to the 3rd part, and not the simplest, the creation of a mail template. Which is actually where we are going to define the formatting of our mail.

To create it, it will depend on the scope of the Workflow.

If the Workflow is shared between all the entities, this will be done in the module “System Administration \ Setup \ E-mail \ System email templates” :



If the Workflow is specific to the entity, this will be done in the module “Organization administration \ Setup \ Organization email templates” :



Both bring us to a similar screen in which we have to create a new line:



Email ID : the Template name, who will be choose in the workflow (next step)

Email description : free text to explain the ID (no impact)

Sender Name : name that will appear in the email as the sender

Sender Email: mandatory address but which will be replaced via SMTP

Then create a line for each of the languages used in your company like this :



Choose the language, then enter the subject. The Subject will be the title of the email.

To insert the body of the email, click on “Email message” or on “Edit”:



And there surprise! D365 asks you to load an HTML file …



A little tour on the web to find how to create an html file and here is a simple test:

Use the following link to compose your own html : https://html5-editor.net/



I’m not hiding you that I haven’t yet figured out how to find and assign the variables between the “%” and that it would be a good idea to dig a little deeper into the subject.

The result of this file is the email below:



Did you think you had finished?

Go still a little effort we are in the last straight line…

We just have to associate our Template in our workflow.

To do this, open the workflow and look at the global parameters of the workflow:



Just to clarify, if you have a header workflow that calls a line workflow, the Template can be only configured in the header workflow.

On the other hand if you configure it only at line level it will not work…

And now, your users will now be able to receive their email notifications 

I hope I didn’t give you a headache too much,