Wednesday, May 30, 2018

Write Event log entry for AOS runtime error in AX

public server static void writeEventLogEntry(str _message)
{
    System.Diagnostics.EventLog ev;
    System.Boolean sourceExists;
    System.Diagnostics.EventLogEntryType entryType;
    System.String msg;
    ;

    new InteropPermission(InteropKind::ClrInterop).assert();
    sourceExists = System.Diagnostics.EventLog::SourceExists('<AOS Runtime>', '<Computer Name>');
    if (sourceExists)
    {
        ev = new System.Diagnostics.EventLog('<Microsoft Dynamics AX AIF>',
'<Computer Name>',
'<AOS Runtime>');
        try
        {
            entryType = CLRInterop::parseClrEnum('System.Diagnostics.EventLogEntryType',
'<Error>');
            msg = _message;
            ev.WriteEntry(msg, entryType);
        }
        catch
        {
            exceptionTextFallThrough();
        }
        if (ev != null)
        {
            ev.Close();
        }
    }

    CodeAccessPermission::revertAssert();
}

Wednesday, May 23, 2018

Changing Business logic in Standard Microsoft Class methods without overlaying in D365 FO


// Changing Business logic in Standard Microsoft Class methods without overlaying

Tag: Dynamics 365 for finance and operations

Steps:

Scope:
è  Changing logic in Standard Microsoft Class methods without overlaying.

Pre Requisites:
è  Base model package should be reference to current package

Example:
Form->PurchTable
Data Source -> PurchLine
Requirement: Need to validate line amount value in purchLine table from system standard validation class method.

è  Let’s consider this below DEVCOCTEST as an assumption for system standard class and methods.
è  In DEVCOCTEST class, Process method contains validation on table (purchLine).
è  Requirement, need to change one of the intermediate line in process method or else changing or adding extra condition in process method.
Note: In Example code, I have written only few lines.
è  In example, currently overlay is not possible for this class method to do. Only way to achieve through extension and chain of command.
è  As assumption, Overlaying locked by Microsoft

//As per our assumption, this is our Microsoft Standard class and methods
//sample code - MS Hard sealed
Class DEVCOCTEST //Parent Class
{
    static DEVCOCTEST construct(PurchLine _purchLine)
    {
        if(_purchLine)
        {
            return new DEVCOCTEST();
        }
        else
        {
            throw error(Error::wrongUseOfFunction(funcName()));
        }

        return null;
    }

    void  Process(PurchLine _purchLine) //Standard Method
    {
        if(_purchLine.LineAmount > 1000)
        {
            throw warning("Purchase Amount limit exceed _ Parent Class");
        }
    }

}

//This is new customize class, created in new model
//With extends system standard class
Note:
è  Here we need to write method which you want to modify requires  business logic
è  If method not written in child class means then system will automatically call parent class method from child class.
class DEVCOCTEST_COCCHILD extends DEVCOCTEST
{
    public void Process(PurchLine _purchLine)
    {
        if(_purchLine.LineAmount > 1000)
        {
            throw warning("Purchase Amount limit exceed _ Child COC Class");
        }
        else
        {
            super(_purchLine); //if requires
        }
    }

}

//This is new customize class, created in new model
//For Extension of assumed Microsoft standard class (DEVCOCTEST)
Note:
è  Here wherever system is constructing DEVCOCTEST class, based on extension of Microsoft standard class using chain of command. System will hit below code
è  After calling, next command we creating new instance of child class (DEVCOCTEST_COCCHILD)

ExtensionOf(classStr(DEVCOCTEST))]
final class DEVCOCTEST_Extension
{
    static DEVCOCTEST construct(PurchLine _purchLine)
    {
        DEVCOCTEST a = next construct(_purchLine);
        a = new DEVCOCTEST_COCCHILD();
        return a;           
    }

}

//This is only for testing, to call

//Code for Event handler
//This is new customize class
//In example, I have consider FORMDATASOURCEEVENT type as Written
class DEVCOCTESTPURCHTABLEFORMEVENTHANDLER
{
    [FormDataSourceEventHandler(formDataSourceStr(PurchTable, PurchLine), FormDataSourceEventType::Written)]
    public static void PurchLine_OnValidatedWrite(FormDataSource sender, FormDataSourceEventArgs e)
    {  
        var purchLine_ds = sender as FormDataSource;
        PurchLine purchLine = purchLine_ds.cursor() as
            PurchLine;

        if(purchLine)
        {
            DEVCOCTEST  coc =   DEVCOCTEST::construct(purchLine);
            coc.Process(purchLine);
        }
        else
        {
            throw Error("Purchase lines Arguments must be called from journal");
        }
    }

}

Sample tested with
Class\PurchLineType_Purch\


Version: (Dynamics 365 for finance and operations)

Standard Class with example:

//Std PurchLineType_Purch class method - Hard seal
ClassPurchLineType_Purch 
Method: ValidateField

Requirement: Here i need to add one more field validation in switch case
public boolean validateField(FieldId _fieldId)
    {
        boolean      ret;

        ret = super(_fieldId);
        switch (_fieldId)
        {
            case fieldNum(PurchLine, TaxItemGroup) :
            case fieldNum(PurchLine, TaxGroup)     :
                if (purchLine.TaxGroup     &&
                    purchLine.TaxItemGroup &&
                    VendTable::find(purchTable.OrderAccount).vatTaxAgent_RU)
                {
                    ret = Tax::checkVATChargeSource_RU(purchTable.purchTable_RU().vatChargeSource_RU, purchLine.TaxGroup, purchLine.TaxItemGroup);
                }
                break;

            case fieldNum(PurchLine, LineNumber):
                var purchLine_orig = purchLine.orig();

                if (purchLine_orig.RecId                                != 0
                    && purchLine.LineNumber                             != purchLine_orig.LineNumber
                    && PurchParameters::find().DisallowLineRenumbering  == NoYes::Yes)
                {
                    ret = checkFailed("@SCM:RenumberingOfLinesNotAllowed");
                }
                break;
        }

        return ret;
    }


//This is new customize class, created in new model
//With extends system standard class
Note:
è  Here we need to write method which you want to modify requires override business logic of that method
è  If method not written in child class means then system will automatically call parent class method from child class.
class PurchLineType_PurchCOC extends PurchLineType_Purch
{
    public boolean validateField(FieldId _fieldId)
    {
        boolean      ret;
        Price   purchPrice;

        switch (_fieldId)
        {
            //Added - start
            case fieldNum(PurchLine, PurchPrice) :
                purchPrice = InventTableModule::find(PurchLine.itemId,ModuleInventPurchSales::Purch,false).Price;
                if(PurchLine.PurchPrice > purchPrice)
                {
                    throw warning("Purch price shouldn't be greater than, Product default purchase price");
                }
                break;
            //Added - end

            Default : 

                break;
        }

        return ret;
    }
}



//This is new customize class, created in new model
//For Extension of Microsoft standard class (PurchLineType_Purch)
Note:
è  Here wherever system is constructing PurchLineType_Purch class, based on extension object concept System will hit below code
è  After calling, next command we creating new instance of child class (PurchLineType_PurchCOC )

[ExtensionOf(classStr(PurchLineType_Purch))]
final public class PurchLineType_Purch_Extension
{
    static PurchLineType construct(PurchLine purchLine, PurchTable  purchTable)
    {
        PurchLineType_Purch coc = next construct(purchLine,purchLine.purchTable());
        coc = new PurchLineType_PurchCOC(purchLine,purchLine.purchTable());
        return coc;
    }

}

Output:
if Current Purchase line price is valid with default purchase price

If current purchase line price is greater than compare with default purchase price

Tuesday, May 22, 2018

Number sequence customization in Dynamics 365 for Finance and Operations


Create a number sequence in a standard module
  • Create new EDT
  • Add a new code block to the loadModule in the NumberSeqModuleXXX class
  • Add a new static method to the parameters table of the module to get the number sequence reference
Using Extension
[ExtensionOf(classStr(NumberSeqModuleCustomer))]
final class NumberSeqModuleCustTest_Extension
{
            protected void loadModule()
            {
                        NumberSeqDatatype datatype = NumberSeqDatatype::construct();
                        next loadModule();                        
datatype.parmDatatypeId(extendedTypeNum(TestId));
datatype.parmReferenceHelp(literalStr('Test ID'));
                        datatype.parmWizardIsContinuous(false);
                        datatype.parmWizardIsManual(NoYes::No);
                       datatype.parmWizardIsChangeDownAllowed(NoYes::No);
                       datatype.parmWizardIsChangeUpAllowed(NoYes::No);
                        datatype.parmSortField(1);                        
datatype.addParameterType(NumberSeqParameterType::DataArea, true, false);
                        this.create(datatype);
            }
}

[ExtensionOf(tableStr(CustParameters))]
final class CustParametersTest_Extension
{
    client server static NumberSequenceReference numRefTestId()
    {
        return NumberSeqReference::findReference(extendedTypeNum(TestId));
    }
}
Create a number sequence in a new module
·        Add a new element to the NumberSeqModule base enum.
·        Create a NumberSeqModule* class for our module.
·        To add a new element to the NumberSeqModule Base Enum we can use an extension.
·        Now we need to create a new class extends with NumberSeqApplicationModule
class NumberSeqModuleTest extends NumberSeqApplicationModule
{
    protected void loadModule()
    {
        NumberSeqDatatype datatype = NumberSeqDatatype::construct();
        datatype.parmDatatypeId(extendedTypeNum(TestId));
        datatype.parmReferenceHelp(literalStr('Test ID'));
        datatype.parmWizardIsContinuous(false);
        datatype.parmWizardIsManual(NoYes::No);
        datatype.parmWizardIsChangeDownAllowed(NoYes::No);
        datatype.parmWizardIsChangeUpAllowed(NoYes::No);
        datatype.parmSortField(1);       
        datatype.addParameterType(NumberSeqParameterType::DataArea, true, false);
        this.create(datatype);
    }

    public NumberSeqModule numberSeqModule()
    {
        return NumberSeqModule::Test;
    }

    [SubscribesTo(classstr(NumberSeqGlobal),delegatestr(NumberSeqGlobal, buildModulesMapDelegate))]
    static void buildModulesMapSubsciber(Map numberSeqModuleNamesMap)
    {
       NumberSeqGlobal::addModuleToMap(classnum(NumberSeqModuleTest), numberSeqModuleNamesMap);
    }
}

Job: (To load all module number sequences)
static void Test_NumSeqLoad(Args _args)
   {
      NumberSeqApplicationModule::loadAll();
   }  



Monday, May 21, 2018

Relax model restrictions to enable the refactoring of over-layering into extensions D365

//Relax model restrictions to enable the refactoring of over-layering into extensions

-> Temporarily allow over-layering in Microsoft models as needed to enable for compilation.
-> Locate the desired model within the C:\AOSService\PackagesLocalDirectory\<Package>\<Model> \Descriptor folder
-> Open the XML file & add below line. For example, DIMENSIONS.xml
<Customization>AllowAndWarn</Customization>
-> Refactor over-layering to extensions and test. Make use of extension capabilities
to eliminate over-layering. If needed, make extensibility requests.
-> Revert the temporary changes to Microsoft models.

Convert Call stack to readable format in D365FO X++

//Input --container _xppCallStack = xSession::xppCallStack();  Public static str POL_formatXppCallStack(container _xppCallStack, int _skipFr...