Monday, October 30, 2023

How to fetch values from records to Include filter using x++ in D365FO

  Hi guys, I have a requirement to fetch the record to include filtered values from the report dialog.



I need to show those values in the report footer.

 Code :

Users can apply filters from the filter screen, and if any new tables are joined or new fields are added. To get those values I have used a query filter.



        str                         value;
	Query                       query;
	QueryFilter                 qf;

	query = qr.query();// getting query from query run.

	int  cnt = query.queryFilterCount();

	// Fetching data from query if any new tables are joined or new Fileds are added
	for (int ds =1; ds<= cnt; ds++)
	{
		qf = query.queryFilter(ds);

		if(qf.value())
		{
			value += strFmt('%1 : %2',
				            qf.field(),
					    qf.value());
		}
	}


The below code is to fetch data from the standard query.

        str                         value;
	Query                       query;
	QueryBuildDataSource        qbds;
	QueryBuildRange             qbr;
	int                         totalDS, totalRanges;

	query             = qr.query();// getting query from query run.
	totalDS           = query.dataSourceCount();

	// Used to fectch data from the standard view query 
	for (int ds =1; ds<= totalDS; ds++)
	{
	    qbds            = query.dataSourceNo(ds);
	    totalRanges     = qbds.rangeCount();

	    for (int r =1; r<= totalRanges; r++)
	    {
		qbr =  qbds.range(r);
			
		if (qbr.value())
		{
                    // Way 1 : 
		    value += strFmt('%1 : %2',
			                        qbr.prompt(),
						qbr.value());
									

		    // Way2 :
		    TableId     tableId 	=  qbds.table();//tableName2Id(qbds.AOTname());
		    DictTable   dictTable   = new SysDictTable(tableId);

		    str fieldname = qbr.AOTname();

		    if(qbr.value())
		    {
			MiscParameters += strFmt('%1 : %2  ',
					dictTable.fieldObject(fieldName2Id(tableId, qbr.fieldName())).label(),
					qbr.value());				   
		    }


		    // Way3 :
		    // Direct Field
		    FieldId  fieldId = qbr.field();
		    if(qbr.value())
		    {
			MiscParameters += strFmt('%1 : %2  ',
						        dictTable.fieldObject(fieldId).label(),
							qbr.value());						  
		    }


		    // Way4 :
		    str     value 	      = strFmt('%1', qbr.value());
		    Map     miscParametersMap = new Map(Types::String, Types::String);// Write this line at out side of loop

		    if (value)
		    {
			if (miscParametersMap.exists(qbr.prompt()))
			{
			    container   con = str2con(miscParametersMap.lookup(qbr.prompt()), ',' , false);

			    if (!conFind(con, value))
			    {
				con += value;
			    }

			    miscParametersMap.insert(qbr.prompt(), con2Str(con));
			}
			else
			{
			    miscParametersMap.insert(qbr.prompt(), value);
			}
		    }

		    if (miscParametersMap.elements())
		    {
			MapEnumerator   mapEnumerator = miscParametersMap.getEnumerator();

			while (mapEnumerator.moveNext())
			{
			    value += (strfmt("%1 : %2  ",
						mapEnumerator.currentKey(),
						mapEnumerator.currentValue()));
			}
		    }
		}
	    }
	}




Keep Daxing!!



Thursday, October 26, 2023

Merge multiple pdf’s into a single pdf using x++ in D365FO

 Got the requirement to Merge multiple pdf’s into a single pdf in D365FO.



To achieve this need to download the (pdfsharp)3rd party DLL from the below link 

https://www.dllme.com/dll/files/pdfsharp#dl.

once DLL is downloaded you have to add at the project reference node.


Write the below code.

using PdfSharp;
internal final class PFDRun
{
    public static void main(Args _args)
    { 
	PdfSharp.Pdf.PdfDocument 	outPutPDFDocument = new PdfSharp.Pdf.PdfDocument();
	PdfSharp.Pdf.PdfDocument 	inputPDFDocument  = new PdfSharp.Pdf.PdfDocument(); 
        PdfSharp.Pdf.PdfPages 		pdfPages;

        container con = ['D:\\Test1.pdf','D:\\Test2.pdf', 'D:\\Test3.pdf'];

        int i, j, pageCount;

	FileName 		pdfFile;
        InteropPermission 	permission;
        str 			errorMessage;

        try
        {
            permission = new InteropPermission(InteropKind::ClrInterop);

            permission.assert();

            for (i = 1; i <= 2; i++)
            {
		// I have written custom class to get the file stream from blob.
                System.IO.MemoryStream pdfStream = MyClass::GetStream(i);

                // pdfStream.Seek(0); // Ensure the stream is at the beginning

		// intialize With stream
                inputPDFDocument = PdfSharp.Pdf.IO.PdfReader::Open(pdfStream, PdfSharp.Pdf.IO.PdfDocumentOpenMode::Import);
				
		// intialize With file name(File to be stored in local folder)
		// pdfFile = conpeek(con, i);
                //inputPDFDocument = PdfSharp.Pdf.IO.PdfReader::Open(pdfFile, PdfSharp.Pdf.IO.PdfDocumentOpenMode::Import);

                outputPDFDocument.set_Version(inputPDFDocument.get_Version());

                pageCount = inputPDFDocument.get_PageCount();
                pdfPages  = inputPDFDocument.get_Pages();

                if (pageCount >0)
                {
                    if (pageCount == 1)
                    {
                        outputPDFDocument.AddPage(pdfPages.get_Item(0));
                    }
                    else
                    {
                        for (j = 1 ; j <= pageCount; j++)
                        {
                            outputPDFDocument.AddPage(pdfPages.get_Item(j-1));
                        }
                    }
                }
            }

            System.IO.MemoryStream outPutStream= new System.IO.MemoryStream();

            outputPDFDocument.Close();
            outputPDFDocument.Save('D:\\Output\\mergedFile.pdf'); // To save in local folder.
            outputPDFDocument.Save(outPutStream, false);

            file::SendFileToUser(outPutStream, 'mergedFile.pdf'); // To download

            CodeAccessPermission::revertAssert();
        }
        catch(Exception::CLRError)
        {
            errorMessage = AifUtil::getClrErrorMessage();

            CodeAccessPermission::revertAssert();

            throw error(errorMessage);
        }
    
    }

}

Sunday, October 22, 2023

Generate Payment Advice report using x++

  Generate Payment Advice report using x++.




Way 1: 
            The system will download the PDF using the Print setting.

Way 2 :

        Get the stream to send the mail or store it in the server. For this, we have to add the parm method in the contract class and assign the value in savePrintArchiveDetails method.


Code:

    public void generatePaymAdvice(LedgerJournalTrans _ledgerJournalTrans)
    {
        Filename                        fileName;
        BankPaymAdviceVendControllerV2  controller  = new BankPaymAdviceVendControllerV2();
        BankPaymAdviceContract          contract;
        SRSPrintDestinationSettings     settings;
        
        fileName = strFmt("%1_%2_PaymemtAdvice.pdf", _ledgerjournalTrans.JournalNum, _ledgerjournalTrans.Voucher);

        contract = BankPaymAdviceContract::newFromPaymentLine(_ledgerjournalTrans, false);
        contract.parmLedgerJournalTransRecId(_ledgerJournalTrans.RecId);

        controller.parmArgs(new Args());
        controller.parmReportName(ssrsReportStr(BankPaymAdviceVendV2, Report));
        controller.parmShowDialog(false);
        controller.parmLoadFromSysLastValue(false);
        controller.parmReportContract().parmRdpContract(contract);
        
        // Way 1
        settings = controller.parmReportContract().parmPrintSettings();
        settings.printMediumType(SRSPrintMediumType::Archive);
        settings.fileName(fileName);
        settings.fileFormat(SRSReportFileFormat::PDF);
        settings.overwriteFile(true);

        controller.startOperation();
    }

Way 2:
Contract class code:

    [ExtensionOf(classStr(SRSPrintArchiveContract))]
    public final class SRSPrintArchiveContract_Extension
    {
	public RefRecId printJobHeaderRecId;

	public RefRecId parmPrintJobHeaderRecId(RefRecId _printJobHeaderRecId = printJobHeaderRecId)
	{
	    printJobHeaderRecId = _printJobHeaderRecId;

    	    return printJobHeaderRecId;
	}

	public RecId savePrintArchiveDetails(container binData)
	{
	    RecId recId = next savePrintArchiveDetails(binData);

	    this.parmPrintJobHeaderRecId(recId);

	    return recId;
	}
    }


Call the below code after controller.Startoperation();

    

    DocuRef  		docuref;
    PrintJobHeader 	printJobHeader;
    select forupdate printJobHeader
        where printJobHeader.RecId == settings.parmSRSPrintArchiveContract().parmPrintJobHeaderRecId();
        
   select forupdate docuref
        where docuref.RefRecId == printJobHeader.RecId
           && docuRef.ActualCompanyId == curExt(); 	



			
   BinData 	        binData 	= new BinData();
   System.IO.Stream 	fileStream 	= DocumentManagement::getAttachmentStream(docuRef);

   File::SendFileToUser(fileStream, fileName);
		
   ttsbegin;
   printJobHeader.delete();
   docuref.delete();
   ttscommit;
    


Get the 'Stream' from docuref table:

 	DocuRef                         docuRef;
	DocuType                        DocuType;
	DocuValue                       docuValue;

	select * from docuRef
	    order by docuRef.createddatetime desc
	    exists join docuValue
		where docuValue.RecId == docuRef.ValueRecId
		    && docuValue.FileType =="PDF"
		    && docuRef.RefTableId == PurchTable.TableId
		    && docuRef.RefRecId      == PurchTable.RecId
		    && docuRef.RefCompanyId  == PurchTable.DataAreaId;
     System.IO.Stream 	fileStream  = DocumentManagement::getAttachmentStream(docuRef);

     File::SendFileToUser(fileStream, fileName);
    // Convert Stream into memory stream

     var memoryStream = new System.IO.MemoryStream();
     fileStream.CopyTo(memoryStream);

        


Keep Daxing!!


Thursday, October 5, 2023

The transaction log for database is full in D365FO

Managing SQL Server data files when the disk is full.

If The database is full will get the below issues

  • When I built the project, I encountered 26 synchronization errors. 
  • The front-end URL is not open, and I am receiving an HTTP Error 503.

So, I checked 'This PC,' and I observed that the MSSQL (H) drive is full. Consequently, I followed the steps below to shrink the database


https://sql2014.wordpress.com/tag/logdata-disk-full/


Step 1: Open SQL Server Management Studio (SSMS) as an administrator.

Step 2: Select the 'AXDB' database, right-click on it, and choose 'Properties.'

Step 3: In the Properties window, navigate to the 'Options' tab, select the 'Recovery model' as 'Simple,' and click 'OK.'

Step 4: Right-click on 'AXDB' again, select 'Tasks,' and then choose 'Shrink.' Under 'Shrink,' select 'Files.'

Step 5: Choose the file type as 'Log' and click 'OK.'

Step 6: Afterward, change the recovery model back to 'Full' and click 'OK.' (Rollback the step 3)


Keep Daxing!!