sabato 13 maggio 2023

D365FO - Detour steps in WMS

 It is needed to create before everything a new menu item for WMS having the following parameters.


Mode should be set as Indirect, Activity code as Data inquiry and a table for lookup must be set (for instance Purchtable).

Then it is required to define mobile device steps as follows (let's assume we want to add a view of the purchase orders while scanning the purchase order id while performing the product receipt).  (Before performing this step the default setup must be automatically created clicking on the button Create default setup).


Once chosen the right stepid, it is needed to click on Add step configuration, and choose a menu item where we want to apply the detour.


Once done we have to add the detours to the step we are creating, clicking on the button Add in the bottom part of the form.


Then we choose the detour to be added from a list.


Then we have to define the field to be sent and received with the detour function (make sure you have created the default fields for all the functions using the Menuitem Warehouse app field names).



The mobile app function will then appears as follows.






lunedì 24 aprile 2023

D365 FFO - Aggiungere campo multiselect lookup

In questo post vediamo come aggiungere un campo di tipo lookup-multiselect  che punta alle delivery modes ad una form STD (CustParameters nel nostro caso). Ho trovato in giro diverse soluzioni, quella che mi sembra logicamente più corretta è quella di memorizzare i valori selezionati in una tabella dedicata, piuttosto che scrivere i valori sul controllo unbound stesso.

1) Per prima cosa creiamo una tabella che conterrà i recId delle delivery mode selezionate.La tabella che chiameremo LILCustParametersDlvMode conterrà quindi un campo "DlvModeRecId" chiave primaria



2) Aggiungiamo ora un controllo di tipo string alla form CustParameters nel tab general e settiamo le seguenti proprietà:

  • Autodeclaration = Yes
  • Name = CustParameters_DlvModes
  • Filter Expression = %1
  • Label = @SYS210

3) Creare un oggetto query avente come datasource la tabella DlvMode e come campi Code e Txt



4)Creare una classe CoC che chiameremo LILCustParametersForm_Extension per estendere la form cust parameter:

 [ExtensionOf(formStr(CustParameters))]  
 final class LILCustParametersForm_Extension  
 {  
   private SysLookupMultiSelectCtrl        msCtrlDlvMode;   
   
   //effettual il binding dei valori tra il campo e la tabella LILCustParametersDlvMode  
   void bindCustParametersDlvMode()  
   {  
     container            catalogIds, catalogValues;  
     LILCustParametersDlvMode    custParametersDlvMode;  
     DlvMode             dlvMode;  
     container            dlvModeIds,  
                     dlvModeValues;  
   
     while select custParametersDlvMode  
       join dlvMode  
       where dlvMode.RecId == custParametersDlvMode.DlvModeRecId  
     {  
       dlvModeIds   += [dlvMode.RecId];  
       dlvModeValues  += [dlvMode.Code];  
     }  
   
     msCtrlDlvMode.set([dlvModeIds, dlvModeValues]);  
   }  
   
   public void saveDlvMode()  
   {  
     int                 counter;  
     RecId                recId;  
     LILCustParametersDlvMode      custParametersDlvMode;  
   
     container  dlvModes = msCtrlDlvMode.get();  
   
     ttsBegin;  
   
     //elimina i valori preesistenti  
     delete_from custParametersDlvMode;  
         
     //Salva i nuovi valori selezionati  
     for (counter = 1; counter <= conlen(dlvModes); counter++)  
     {  
       recId = any2int64(conpeek(dlvModes, counter));  
   
       if (recId)  
       {  
         custParametersDlvMode.clear();  
   
         custParametersDlvMode.DlvModeRecId = recId;  
   
         custParametersDlvMode.insert();  
       }  
     }  
   
     ttsCommit;   
   }
   
   private void initAndBindCustParametersDlvMode()
   {
       msCtrlDlvMode = SysLookupMultiSelectCtrl::construct(this, CustParameters_DlvModes, queryStr(LILDlvModeQuery));

       this.bindCustParametersDlvMode();
   }
   
   public void init()  
   {  
     next init();  
   
     this.initAndBindCustParametersDlvMode();
   }  
 }    

5)Sottoscrivere l'evento modified del controllo CustParameters_DlvModes per gestire l'inserimento dei valori:
 class LILCustParametersFormEH  
 {    
   [FormControlEventHandler(formControlStr(CustParameters, CustParameters_DlvModes), FormControlEventType::Modified)]  
   public static void CustParameters_DlvModes_OnModified(FormControl sender, FormControlEventArgs e)  
   {  
     FormRun formRun = sender.formRun() as FormRun;  
   
     formRun.saveDlvMode();  
   }  
 }  

lunedì 17 aprile 2023

Keep CR/LF from SQL Grid to copy

 

It's just a kind suggestion.

Could be happen, copying from SQL grid to text editor the carriage return or line feed are missing

 


Go to SQL TOOLS – Options … ENABLE the flag
Retain CR/LF on copy or save



Open a NEW Tab (in order to load the new parameters), now it works.

 


enjoy


 

 

giovedì 23 marzo 2023

FLIGHT – KILL SWITCH hidden parts insidious and useful at the same time, how to befriend them.

I am merely offering food for thought. 


The main part: the features are probably the most famous

  • feature usually is a larger piece of business logic and is typically controlled by the keyuser in the system/company. By the Feature we can enable an entire logical process or add a new helpful tools (like grid filter, column mover etc).

For example “Include waiting records in history cleanup tasks”

This feature lets you include waiting records when running the "Purchase update history cleanup" … (omiss) periodic tasks. It adds a new option called "All" to the "Clean up" drop-down list for the dialogs that launch each of these tasks. Select "All" to include the waiting records.

In other hand a feature is look like a new package (add or fix) in our system.

Microsoft Doc:
Feature management overview - Finance & Operations | Dynamics 365 | Microsoft Learn

We have different meaning for flight and Kill Switch.

We can both assume them as a switch.

FLIGHT:

By flights, we can specifically enable (by atomic way) small pieces of code.

So, using flight we can handle on specific points in the code which will enable changes in the system functionality.

They are separate from the customer control provided by Feature Management.

SYSFLIGHTING - Microsoft DOC

KILL SWITCH

By Kill switch is possible to turn application flight ON and OFF, is not necessary to deploy/rebuild the any code.

The code remains the same but change the logic behavior immediately.

The only way to disable a flight is with a kill switch.


  • How to enable a Kill Swith (by SQL script):

    INSERT INTO SYSFLIGHTING (FLIGHTNAME, ENABLED) VALUES ('MyKillSwitchName_KillSwitch', 1)

  • How to check if Flight is enable:
    select ENABLED, * from SysFlighting where FLIGHTNAME = 'MyFlightName'

  • How to enable a Flight
    insert into dbo.SYSFLIGHTING(FLIGHTNAME, ENABLED, FLIGHTSERVICEID) values ('MyFlightName', 1, 12719367)

  • Example Flight switch code

Non è stato fornito nessun testo alternativo per questa immagine

My consider: always try first in test environment and ask to Support. Change these elements can be produce irreversible change of data.

So, I wish all good luck.

enjoy







mercoledì 14 dicembre 2022

D365FFO – Send email and attachment by code

Following these steps it possible to create an Excel file from code, and send it by email as an attachment without  saving it in a physically folder.

From Cloud and OnPrem environment.

Example x++ code:

 using OfficeOpenXml.Style;   
 using OfficeOpenXml.Table;   
 using System.Net;    
 using Microsoft.Dynamics.ApplicationPlatform.Services.Instrumentation;   
 using Microsoft.DynamicsOnline.Infrastructure.Components.SharedServiceUnitStorage;    
 using Microsoft.Dynamics.ApplicationPlatform.Environment;    
 using Microsoft.Dynamics.AX.Framework.FileManagement;   
    
 public void createExcelandSendByEmail()   
 {   
      #Properties   
      #AOT   
      #File   
   
      str emailSenderName;   
      str emailSenderAddr;   
      str emailSubject;   
      str emailBody;   
   
      emailSubject      = "to set";   
      emailBody           = "to set";   
      emailSenderAddr = "to set";   
      emailSenderName = "to set";    
      System.IO.Stream workbookStream = new System.IO.MemoryStream();   
      System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();    
   
      using(var package = new OfficeOpenXml.ExcelPackage(memoryStream))   
      {    
           var worksheets = package.get_Workbook().get_Worksheets();   
           var worksheet = worksheets.Add("Sheet1");   
           var cells  = worksheet.get_Cells();       
           var currentRow = 1;    
   
           /*-------HEADER PART -START- -------*/    
   
           var cell = cells.get_Item(currentRow,1);    
           cell.set_Value("First Value");    
           cell=null;   
   
           cell = cells.get_Item(currentRow,2);   
           cell.set_Value("Second Value");    
           cell=null;   
   
           /*-------HEADER PART -END- -------*/    
   
           /*-------RECORD -START- -------*/    
   
           currentRow++; //    
   
           cell = null;  
   
           cell= cells.get_Item(currentRow, 1);   
           cell.set_Value("Value");   
   
           cell= null;   
   
           cell= cells.get_Item(currentRow, 2);   
           cell.set_Value("Value");   
   
           /*-------RECORD -END- -------*/   
   
           package.Save();   
   
           var messageBuilder = new SysMailerMessageBuilder();   
             
           messageBuilder.addTo(listRecipient);   
           messageBuilder.setSubject(emailSubject);   
           messageBuilder.setBody(emailBody);   
           messageBuilder.Setpriority("add your value");   
             
           messageBuilder.addAttachment(memoryStream, "Excel_File_Name.xlsx"); // Attach the Excel file    
           SysMailerFactory::sendNonInteractive(messageBuilder.getMessage()); // Send email available in batch    
      }     
 }
Parameters:

smtp.office365.com
587
SSL/TLS required a YES




Set the Outlook address in the execution user setup ( the user who will perform the process).
SMTP provider Id.


enjoy

giovedì 3 novembre 2022

D365FFO - Salvare SSRS report come file PDF e caricarlo su una Azure Storage

In questo post vediamo come generare il file PDF di un report (Fattura a testo libero in questo caso) e fare l'upload su di una Azure storage.

Ho preso spunto da questo post:

https://meritsolutions.com/render-report-memory-stream-d365-aka-ax7/

ed ho aggiunto la parte per l'upload su Azure:

 using Microsoft.Azure;  
 using Microsoft.WindowsAzure.Storage;  
 using Microsoft.WindowsAzure.Storage.Blob;  
 using Microsoft.WindowsAzure.Storage.File;  
   
 class LILTestClassPDFUpload  
 {  
   public static void main(Args _args)  
   {  
     Args args;  
     CustInvoiceJour CustInvoiceJour;  
   
     select firstonly    CustInvoiceJour  
       where CustInvoiceJour.InvoiceId == "000051"  
                && CustInvoiceJour.SalesId == "";  
   
     Filename fileName = strFmt("%1_%2%3",curExt(),CustInvoiceJour.InvoiceId,".pdf");  
     FreeTextInvoiceController controller = new FreeTextInvoiceController();  
     FreeTextInvoiceContract contract = new FreeTextInvoiceContract();  
     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();  
             
           //esempio:  
           //mystorageAccountName.file.core.windows.net\myRootFolder\mySubFolder  
     str           accountName   = "mystorageAccountName";   
     str           key       = "Er0kY1KUDX9/D3tunAD6twYaBT6ux3nJp...etc..";  
     str           rootFolder   = "myRootFolder";  
     str           custLinkFolder = "mySubFolder";  
     ;  
   
     args = new Args();  
     args.record(CustInvoiceJour);  
     contract.parmCustInvoiceJourRecId(CustInvoiceJour.RecId);  
   
     // Provide details to controller and add contract  
     controller.parmArgs(args);  
     controller.parmReportName(ssrsReportStr(TTL_FreeTextInvoice, Report));  
     controller.parmShowDialog(false);  
     controller.parmLoadFromSysLastValue(false);  
     controller.parmReportContract().parmRdpContract(contract);  
     // Provide printer settings  
     settings = controller.parmReportContract().parmPrintSettings();  
     settings.printMediumType(SRSPrintMediumType::File);  
     settings.fileName(fileName);  
     settings.fileFormat(SRSReportFileFormat::PDF);  
   
     // Below is a part of code responsible for rendering the report  
     controller.parmReportContract().parmReportServerConfig(SRSConfiguration::getDefaultServerConfiguration());  
     controller.parmReportContract().parmReportExecutionInfo(executionInfo);  
   
     srsReportRunService.getReportDataContract(controller.parmreportcontract().parmReportName());  
     srsReportRunService.preRunReport(controller.parmreportcontract());  
     reportParametersMap = srsReportRunService.createParamMapFromContract(controller.parmReportContract());  
     parameterValueArray = SrsReportRunUtil::getParameterValueArray(reportParametersMap);  
   
     srsProxy = SRSProxy::constructWithConfiguration(controller.parmReportContract().parmReportServerConfig());  
     // Actual rendering to byte array  
     reportBytes = srsproxy.renderReportToByteArray(controller.parmreportcontract().parmreportpath(),  
       parameterValueArray,  
       settings.fileFormat(),  
       settings.deviceinfo());  
   
           //upload file...  
     if (reportBytes)  
     {  
       System.IO.MemoryStream stream = new System.IO.MemoryStream(reportBytes);  
   
       str filetemppath = File::SendFileToTempStore(stream,fileName);  
       System.IO.Stream fileStream = File::UseFileFromURL(filetemppath);  
   
       var storageCredentials = new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials(accountName, key);  
       CloudStorageAccount storageAccount = new Microsoft.WindowsAzure.Storage.CloudStorageAccount(storageCredentials, true);  
       CloudFileClient fileClient = storageAccount.CreateCloudFileClient();  
       CloudFileShare share = fileClient.GetShareReference(rootFolder);  
                  
       if (share.Exists(null, null))  
       {  
         CloudFileDirectory rootDir = share.GetRootDirectoryReference();  
         CloudFileDirectory fileDir = rootDir.GetDirectoryReference(custLinkFolder);  
                       
         if (fileDir.Exists(null, null))  
         {  
           CloudFile cfile = fileDir.GetFileReference(fileName);  
           
           if (cfile.Exists(null, null))  
           {  
             throw error("@SYS95668");  
           }  
           else  
           {  
             str a = File::SendFileToTempStore(stream,fileName);  
             try  
             {  
               cfile.UploadFromStream(fileStream,null,null,null);  
             }  
             catch(Exception::Error)  
             {  
               throw error("Error during file upload");  
             }  
           }  
         }  
       }  
     }  
   }  
 }  

mercoledì 28 settembre 2022

AX 2012 - QR code su SSRS

In questo post vediamo come aggiungere un QR ad un report SSRS. Per fare ciò AX mette a disposizione un componente esterno.

Per prima cosa occorre creare nella tabella temporanea del report un campo di tipo Bitmap che chiameremo QRCode (possiamo per esempio copiare il campo CompanyLogo presente nella tabella del report della fattura).

Con questo metodo inserito nella nostra DP possiamo generare il BitMap da associare al nostro campo QRCode:

protected Bitmap getQRCode()
{
    Bindata                 bindata = new Bindata();
    System.Drawing.Bitmap   obj;
    Filepath                filepath,
                        filePathName;                
    container               con;
    Microsoft.Dynamics.QRCode.Encoder   encoder;
    FileIoPermission  filepermission; 

    filepath = @'\\MyServer\Temp\';
        
    filePathName = System.IO.Path::Combine(filepath
                                            ,strFmt("%1.bmp","img"));
        
    filepermission = new FileIoPermission(filePathName, 'rw');
    filepermission.assert();
        
    encoder   = new Microsoft.Dynamics.QRCode.Encoder();
    obj = new System.Drawing.Bitmap(encoder.Encode("StringToEncode"));
    obj.Save(filePathName,System.Drawing.Imaging.ImageFormat::get_Bmp());
    bindata.loadFile(filePathName);
    con = bindata.getData();
        
    CodeAccessPermission::revertAssert();
   
    return con;
} 

Sul Design del report dobbiamo poi definire un controllo di tipo image, possiamo copiare dai report standard che visualizzano i loghi (Conferma ordine, Fattura etc...). Ecco il risultato: