Showing posts with label Extension. Show all posts
Showing posts with label Extension. Show all posts

Tuesday, April 23, 2024

Iterate through extended child classes dynamically using X++

/// <summary>
/// Base class for search
/// </summary>
class searchBaseClass
{
    const private Integer priorityMax = 99;

    /// <summary>
    /// Method that searches for values
    /// </summary>
   
    protected void search(//your parmeters)
    {
    }

    /// <summary>
    /// Method that indicates the class priority and therefor the number in which it is executed
    /// </summary>
    /// <returns>
    /// An integer indicating the class priority and therefor the number in which it is executed
    /// </returns>
    protected Priority priority()
    {
        return searchBaseClass::PriorityMax;
    }

    /// <summary>
    /// Method that creates a map of search classes to be executed in a priorized way
    /// </summary>
    /// <returns>
    /// A map containing class objects to execute
    /// </returns>
    private Map createExecutionMap()
    {
        DictClass                       dictClass;
        searchBaseClass    		searchBase;
        ListEnumerator                  listEnumerator;
        List                            listClass = new DictClass(classnum(searchBaseClass)).extendedBy();
        Map                             mapExecutionClasses = new Map(Types::Integer, Types::Class);

        listEnumerator = listClass.getEnumerator();
        while (listEnumerator.moveNext())
        {
            dictClass = new DictClass(listEnumerator.current());
            if (dictClass)
            {
                searchBase = dictClass.makeObject();
                if (searchBase)
                {
                    // Add class object to execution list unless the priority is already added
                    if (!mapExecutionClasses.exists(searchBase.priority()))
                    {
                        mapExecutionClasses.insert(searchBase.priority(), searchBase);
                    }
                    else
                    {
                        warning(strFmt("SearchSkipped", dictClass.name(), searchBase.priority()));
                    }
                }
            }
        }

        return mapExecutionClasses;
    }

    /// <summary>
    /// Method that run through all classes that searches for data
    /// </summary>
    public void run()
    {
        searchBaseClass  		  	  searchBase;
        Map                           mapExecutionClasses;

        mapExecutionClasses = this.createExecutionMap();

        for (int i = 1; i <= searchBase::priorityMax; i++)
        {
            if (mapExecutionClasses.exists(i))
            {
                searchBase = mapExecutionClasses.lookup(i);
                if (searchBase)
                {
                    searchBase.search();
                }
            }
        }
    }

}


/// <summary>
/// child Class searching 
/// </summary>
class searchChildClass extends searchBaseClass
{
    /// <summary>
    /// Method that indicates the class priority and therefor the number in which it is executed
    /// </summary>
    /// <returns>
    /// An integer indicating the class priority and therefor the number in which it is executed
    /// </returns>
    protected Priority priority()
    {
        return 1;
    }

    /// <summary>
    /// Method searching
    /// </summary>
  
    protected void search(//your parmeters)
    {
        //your logic
    }

Monday, December 12, 2022

D365FO IDisposable context class

Scenario: pass additional parameter to a method that doesnt have a parametr signature 
After the execution of using code block , Dispose method will be called.

IDisposible is a singleton instance of a class per session, for example if you open two different tabs and run the code you will get two different instance of the class
class DocTypeFile_CUSTOM_Context implements System.IDisposable
{
    static DocTypeFile_CUSTOM_Context  docTypeFileContext;

    public  boolean overrideDefaultDocType = false;
    public boolean printSalesInvoiceReport;
    public boolean printSalesConfReport;
   // public PrintMgmtDocumentType printMgmtDocumentType;
   public DocuTypeId doucTypeId = strMin();
  
    public void new()
    {
        if(docTypeFileContext)
        {
            throw error("NestingIsNotSupported");
        }
        docTypeFileContext = this;
    }

    public void dispose()
    {
        docTypeFileContext = null;
    }

    static public DocTypeFile_CUSTOM_Context current()
    {
        return  docTypeFileContext;
    }

}
/// <summary>
///     To handle the report design to print based on caller
/// </summary>
[ExtensionOf(classStr(SalesInvoiceController))]
final class SalesInvoiceController_CUSTOM_Extension
{
    /// <summary>
    ///     choose report design
    /// </summary>
    protected void outputReport()
    {
		//initalize disposible context class 
        using (var docTypeFileContext = new DocTypeFile_CUSTOM_Context())
        {
            SRSPrintDestinationSettings srsPrintDestinationSettings = formLetterReport.getCurrentPrintSetting().parmPrintJobSettings();
           
            if (srsPrintDestinationSettings.printMediumType() == SRSPrintMediumType::File)
            {
			   docTypeFileContext.overrideDefaultDocType = true;
               docTypeFileContext.doucTypeId = "File";
            }

            next outputReport();
        }
    }

}
[ExtensionOf(tableStr(DocuType))]
final class DocuType_CUSTOM_Extension
{
    static DocuTypeId typeFile()
    {
        DocuTypeId docuTypeId;
        docuTypeId = next typeFile();

        DocTypeFile_CUSTOM_Context  docTypeFileContext = DocTypeFile_CUSTOM_Context::current();

        if(docTypeFileContext && docTypeFileContext.overrideDefaultDocType)
        {
            docuTypeId = docTypeFileContext.doucTypeId;
        }

        

        return docuTypeId;
    }

}

Wednesday, November 30, 2022

D365FO: Zip/Unzip memory stream to byte[] array X++

Sometimes we have to deal with big file streams, during that scenario there is an option to zip/unzip the file using gzip in X++.

for example: Pass files larger than 25MB to logic apps, logic app has a file size limit of 25 MB
using System.IO;
using System.IO.Compression;


class ZipUnzip
{
    public static System.Byte[] Compress(System.Byte[] data)
    {
        var compressedStream = new MemoryStream();
        var zipStream = new GZipStream(compressedStream, CompressionMode::Compress);
        zipStream.Write(data, 0, data.Length);
        zipStream.Close();
        return compressedStream.ToArray();
    }

    public static System.Byte[] Decompress(System.Byte[] data)
    {
        var compressedStream = new MemoryStream(data);
        var zipStream = new GZipStream(compressedStream, CompressionMode::Decompress);
        var resultStream = new MemoryStream();

        System.Byte[] buffer = new System.Byte[4096]();
        int read;

        do
        {
            read = zipStream.Read(buffer, 0, buffer.Length);
            resultStream.Write(buffer, 0, read);
        }
        while(read);

        return resultStream.ToArray();
    }

}

Thursday, November 24, 2022

D365FO:Cancel Purchase order through X++

 Create a new class and extend standard PurchCancel class

class PurchCancel_CUSTOM extends PurchCancel
{
    public void cancelPO(PurchTable _PurchTable)
    {
        PurchCancel_CUSTOM purchCancel = PurchCancel_CUSTOM::construct();
        purchCancel.parmPurchTable(_PurchTable);
       // purchTable = _PurchTable;
        purchCancel.run();
    }

    static PurchCancel_CUSTOM construct()
    {
        return new PurchCancel_CUSTOM();
    }

    void new()
    {
        super();
    }
}

Saturday, April 24, 2021

Sysoperations frameworks Tips and tricks in AX

    protected ClassDescription defaultCaption()
    {
        return "@CashManagement:CashFlowTimeSeriesInitializeControllerCaption";
    }

    public ClassDescription caption()
    {
        return this.defaultCaption();
    }

    /// <summary>
    /// Indicates if the class must run in batch or not.
    /// </summary>
    /// <returns>
    /// Always returns false.
    /// </returns>
    /// <remarks>
    /// This method must be in this class because it is called from the <c>dialogRunbase</c> class.
    /// </remarks>
    public boolean mustGoBatch()
    {
        return false;
    }

    /// <summary>
    /// Determines whether the job can be executed in batch.
    /// </summary>
    /// <returns>
    /// false if the job cannot be executed in batch; otherwise, true.
    /// </returns>
    public boolean canGoBatch()
    {
        return false;
    }

    /// <summary>
    /// Sets whether to show the batch tab or not.
    /// </summary>
    /// <param name = "_showBatchTab">Flag to identify whether to show the batch tab or not.</param>
    /// <returns>False for batch tab to be invisible</returns>
    public boolean showBatchTab(boolean _showBatchTab = showBatchTab)
    {
        return false;
    }
	
	public boolean showBatchRecurrenceButton(boolean _showBatchRecurrenceButton = showBatchRecurrenceButton)
    {
        return false;
    }

Monday, April 12, 2021

Register override lookup in D365fo from form datasource

/// <summary>
/// Extension of the form 'SalesTable' data source 'SalesLine'.
/// </summary>
[ExtensionOf(formDataSourceStr(SalesTable, SalesLine))]
final class Sales_SalesTableForm_SalesLineDS_Extension
{
    /// <summary>
    /// COC construct to handle standard datasource init method
    /// </summary>
    /// <remarks>
    /// Method overriden to add custom logic
    /// </remarks>
    void init()
    {
        next init();

        this.m_Init();
    }

    /// <summary>
    /// COC Implementation for additional logic on init method
    /// </summary>
    void m_Init()
    {
        this.sm_ReasonCodeLookupRegisterOverride();
    }

    /// <summary>
    /// SM reason code lookup override method
    /// </summary>
    public void m_ReasonCodeLookupRegisterOverride()
    {
        FormDataSource salesLineDataSource = element.dataSource(formDataSourceStr(SalesTable, SalesLine)) as FormDataSource;
        SM_Sales_SalesTableFormRegisterMethods formRegisterMethods = new SM_Sales_SalesTableFormRegisterMethods();

        salesLineDataSource.object(fieldNum(SalesLine, SM_ReasonCode)).registerOverrideMethod(methodStr(FormDataObject, lookup),
                                methodStr(M_Sales_SalesTableFormRegisterMethods, lookupReasoncode ),
                                formRegisterMethods);
              
    }

}

/// <summary>
/// Form 'SalesTable', data source 'SalesLine' register override method implementation
/// </summary>
class Sales_SalesTableFormRegisterMethods
{
    /// <summary>
    /// Overrides <c>SM_Reasoncode</c> field lookup
    /// </summary>
    /// <param name = "_formStringControl">The control that is bound to the <c>SM_Reasoncode</c> field.</param>
    public void lookupReasoncode(FormStringControl _formStringControl)
    {
        FormRun formRun = _formStringControl.formRun();

        SalesTable salesTableLoc = formRun.dataSource(formDataSourceStr(SalesTable, SalesTable)).cursor() as SalesTable;

        SalesLine::m_lookupReasoncode(_formStringControl, salesTableLoc.DlvReason);
    }

}

/// <summary>
    /// Overrides <c>SM_Reasoncode</c> field lookup
    /// </summary>
    /// <param name = "_formStringControl">The control that is bound to the <c>SM_Reasoncode</c> field.</param>
    public static void lookupReasoncode(FormStringControl _formStringControl, DlvReasonId _dlvReason)
    {
        Query query = new Query();

        QueryBuildDataSource queryBuildDataSource = query.addDataSource(tableNum(ReasonTable));
        queryBuildDataSource.addSelectionField(fieldnum(ReasonTable, Reason));

        if (SM_CustParameters::find().ActivateReasonCode)
        {
            container conReasoncode;
            ReasonTable reasonTable;
            SM_DeliveryReasonReasonCodeCombination deliveryReasonReasonCodeCombination;

            while select ReasonCode from deliveryReasonReasonCodeCombination
                exists join reasonTable
                where reasonTable.Reason == deliveryReasonReasonCodeCombination.ReasonCode
                && deliveryReasonReasonCodeCombination.DlvReasonId == _dlvReason
                && reasonTable.SM_ActivateReasonCode == NoYes::Yes
            {
                conReasoncode += deliveryReasonReasonCodeCombination.ReasonCode;
            }

            str reasoncodes = conReasoncode != conNull() ? con2Str(conReasoncode) : SysQuery::valueEmptyString();

            queryBuildDataSource.addRange(fieldnum(ReasonTable, Reason)).value(reasoncodes);
        }
       
        SysTableLookup lookup = SysTableLookup::newParameters(tablenum(ReasonTable), _formStringControl);

        lookup.addLookupfield(fieldnum(ReasonTable, Reason));
        lookup.addLookupfield(fieldnum(ReasonTable, Description));

        lookup.parmQuery(query);
        lookup.performFormLookup();
    }

Sunday, April 11, 2021

Access public variables in extension class using chain of command

When you wrap a method, you can also access public and protected methods
and variables of the base class.
CustInvoiceJour is a base class variable hence you will be able to access it. Eg.

[ExtensionOf(classStr(SalesConfirmJournalCreate))]
Final class SalesConfirmJournalCreate_Extension
{
protected void createJournalHeader()
{
next createJournalHeader();
//It's as simple as this:
custConfirmJour.SalesBalance = 0;
}
if gets an error, It's an old bug with variable names. Declare new one and use it:
protected void createJournalHeader()
{
next createJournalHeader();
CustConfirmJour custConfirmJourLocal =
custConfirmJour;
custConfirmJourLocal.MyField = 'blah-blah';
}
}

Add new fields in sysoperation dialog and get the values in controller class in AX

UIBuilder class

[ExtensionOf(classstr(ExchangeRateImportUIBuilder))]
final class PDPExchangeRateImportUIBuilder_Extension
{
    public FormBuildIntControl      intCtrl;
    public FormBuildCheckBoxControl booleanCtrl;

    public void build()
    {
        next build();

        DialogField                 dialogField;
        DialogField                 dialogFieldNoYesId;
        ExchangeRateImportRequest   dataContract1;
        FormBuildCheckBoxControl    exchangeRateFromPreviousDayControl;

        dataContract1    = this.dataContractObject();

       //  creating this field if standared code skips it to avoid type cast error in contoller at runtime
        if(exchangeRateFromPreviousDayControl == null)
        {
            dialogField = dialog.addField(extendedtypestr(ExchangeRateFromPreviousDay));
            exchangeRateFromPreviousDayControl = dialogField.control();
            exchangeRateFromPreviousDayControl.value(dataContract1.parmExchangeRateFromPreviousDay());
            exchangeRateFromPreviousDayControl.allowEdit(false);
            exchangeRateFromPreviousDayControl.visible(false);
        }

        dialogField     = dialog.addField(extendedtypestr(NumberOf),"integer","integer");
        intCtrl         = dialogField.control();
        intCtrl.value(dataContract1.parmInteg());

        dialogField     = dialog.addField(extendedtypestr(NoYesId),"boolean","boolean");
        booleanCtrl     = dialogField.control();
        booleanCtrl.value(dataContract1.parmNoYesId());
    }

}

Contract class

[ExtensionOf(ClassStr(ExchangeRateImportRequest))]
[DataContractAttribute]
final class PDPExchangeRateImportRequest_Extension
{
    public int     integ;
    public NoYesId noYesValue;

    /// <summary>
    /// data memeber attribute of Integ field
    /// </summary>
    /// <param name = "_integ">integ</param>
    /// <returns>int</returns>
    [DataMemberAttribute]
    public int parmInteg(int _integ = 0)
    {
        if (!_integ)
        {
            integ = _integ;
        }

        return integ;
    }

    /// <summary>
    /// NoyesId field
    /// </summary>
    /// <param name = "_noYesValue">NoYesValue</param>
    /// <returns>boolean</returns>
    [DataMemberAttribute]
    public NoYesId parmNoYesId(NoYesId _noYesValue = NoYes::No)
    {
        if (!_noYesValue)
        {
            noYesValue = _noYesValue;
        }

        return _noYesValue;
    }

}

Controller class

[ExtensionOf(ClassStr(ExchangeRateImportController))]
final class PDPExchangeRateImportController_Extension
{
    public const str numberof = 'Fld11_1';
    public const str noYesValue = 'Fld12_1';


    public void getFromDialog()
    {
        next getFromDialog();

        FormRun                             theDialogForm;
        ExchangeRateImportRequest           exchangeRateImportRequest;
        ExchangeRateProviderFactory         factory;

        FormIntControl intCtrl;
        FormCheckBoxControl booleanCtrl;
        FormCheckBoxControl exchangeRateFromPreviousDayControl;
       
        exchangeRateImportRequest = this.getDataContractObject(classStr(ExchangeRateImportRequest));
        theDialogForm = this.dialog().formRun();

        intCtrl = theDialogForm.control(theDialogForm.controlId(numberof));

        booleanCtrl = theDialogForm.control(theDialogForm.controlId(noYesValue));

        exchangeRateImportRequest.parmInteg(intCtrl.value());

        exchangeRateImportRequest.parmExchangeRateTypeRecId(booleanCtrl.value());

        Info(strFmt("%1--%2",intCtrl.value(), booleanCtrl.value()));

    }

}

Table browser URL in D365FO

Critical Thinking icon icon by Icons8