Friday, June 8, 2018

IDE MORPHX - Access only on Code Read and Debug in AX 2012

Access Restriction on Morphx IDE Development Environment in AX 2012

There is one option to remove the X++ development license from the environment. But doing that means that nobody will be able to even view the code

In this Development, User can able to only Read and Debug the code, will not able to do Create/Update/Delete objects. Even if user having system administrator rights.

Development:
Steps:
·         Add New Enum element --> SysVersionControlType
·         Create New Class --> SANRestrictAXDevAccess extends SysVersionControlSystem
o   Construct Method for new class
o   Override methods
      (Note: Copy below method from class "SysVersionControlSystem" and return code as Below)
Code: return false;
§  allowDelete
§  allowCreate
§  allowEdit
§  allowMoveToModel
§  allowRename
§  isTmpItemOpenedForDelete
                        Code: return true;
§  applyGlobalSettings
§  ideIntegration   
·         Go to Class--> SysVersionControlSystem --> Method (NewType)
Add your new class (SANRestrictAXDevAccess), construct method in Switch case

Setup Changes Requires:
·         AX--> Development Workspace-->Version Control--> Version control Parameters
·         Select new created enum element from version control type list
·         And Version Control status should be enable

Error on Data Entity General Journals Import in Dynamics 365 for finance and operations

Issue/Cause:

1.      In Data Entities, System is inserting GL journal lines without creating reference in header line. In import document (excel or CSV) input for journal name is invalid with master Journal Names setup. 

Resolution: (Code Fix) .BUG
Adding separate new validation method to validate journal Names (validateJournalName).

In default Method (copyCustomStagingToTarget) in LedgerJournalEntity, adding newly added validation method in to it.

Note: 
Overlay requires (Not recommended)
Contact MS support or else wait for until MS releasing this code bug fix

Code: 
//Add new method in LedgerJournalEntity
//Added by Sangeeth - start
private static void validateJournalName(DMFDefinitionGroupExecution _dmfDefinitionGroupExecution, DMFStagingValidationLog _log)
    {
        // Put any rows that are not in Ledger journal name into error state
        LedgerJournalName ledgerJournalName;
        LedgerJournalEntityStaging staging;

        LedgerJournalEntityLog entityLog;
       
        ErrorMsg error = "Journal Name is invalid";

        insert_recordset entityLog (LedgerJournalEntityRecId, ErrorMessage)
            select RecId, error from staging
            where staging.DefinitionGroup == _dmfDefinitionGroupExecution.DefinitionGroup
               && staging.ExecutionId == _dmfDefinitionGroupExecution.ExecutionId
               && staging.TransferStatus == DMFTransferStatus::NotStarted
            exists join ledgerJournalName
                where staging.JournalName != ledgerJournalName.JournalName;


        // write errors to DMF validation log
        update_recordset staging
            setting TransferStatus = DMFTransferStatus::Error
        exists join entityLog
            where staging.RecId == entityLog.LedgerJournalEntityRecId;

        FieldName fieldName = fieldStr(LedgerJournalEntityStaging, JournalName);
        TableName tableName = tableStr(LedgerJournalEntityStaging);
        TableName entityName = "@GeneralLedger:LedgerJournalEntityLabel";
        DMFSourceTarget sourceTarget = DMFSourceTarget::Target;

        insert_recordset _log (DefinitionGroupName, ExecutionId, StagingRecId, StagingTableName, StagingColumnName, SourceTarget, EntityName, ErrorMessage)
            select DefinitionGroup, ExecutionId, RecId, tableName, fieldName, sourceTarget, entityName from staging
        join ErrorMessage from entityLog
            where staging.RecId == entityLog.LedgerJournalEntityRecId;
    }

//Added by Sangeeth - End

//Add below code to validate in
method: copyCustomStagingToTarget

  
//Old code
LedgerJournalEntity::validateJournalType(_dmfDefinitionGroupExecution, log);
LedgerJournalEntity::validateAndUpdateExchangeRates(_dmfDefinitionGroupExecution, log);
LedgerJournalEntity::validateAccountTypes(_dmfDefinitionGroupExecution, log);

  
//Replace by
LedgerJournalEntity::validateJournalName(_dmfDefinitionGroupExecution, log);
LedgerJournalEntity::validateJournalType(_dmfDefinitionGroupExecution, log);
LedgerJournalEntity::validateAndUpdateExchangeRates(_dmfDefinitionGroupExecution, log);

LedgerJournalEntity::validateAccountTypes(_dmfDefinitionGroupExecution, log); 

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
Class: PurchLineType_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.

Thursday, April 12, 2018

Hyperlinks for SSRS report in D365

Navigate report design->text box  properties-> Action

Example:1  - Single parameter filter
=Microsoft.Dynamics.Framework.Reports.BuiltInMethods.GenerateDrillThroughLink(
Parameters!AX_ReportContext.Value, 
Parameters!AX_UserContext.Value, 
"MenuItemName", 
"Display",  //Menu item type
"Table Name", 
"Field Name", "Field Value")

Example:2 - Multi-parameter Filter
=Microsoft.Dynamics.Framework.Reports.BuiltInMethods.GenerateDrillThroughLink(
Parameters!AX_ReportContext.Value, 
Parameters!AX_UserContext.Value, 
"MenuItemName", 
"Display", //Menu item type
"Table Name", 
"Field Name", "Field Value"
"Field Name", "Field Value")

Add this Method in your FORM. (where Menutiem denotes)

public boolean isRootNavigable()
{
return true;
}

Tuesday, April 3, 2018

MR Data Mart Schedule interval information through SQL query

//Get list of scheduled MR batch job

USE ManagementReporter

SELECT
Trig.Id,
Task.Name,
Trig.Interval,
   CASE Trig.[UnitOfMeasure]
   WHEN 1 THEN 'Seconds'
   WHEN 2 THEN 'Minutes'
   WHEN 3 THEN 'Hours'
   WHEN 4 THEN 'Days'
End as UnitOfMeasure

FROM [Connector].[Map] Map

Inner join [Scheduling].[Task] Task
               on Map.MapId = Task.Id
Inner join [Scheduling].[Trigger] Trig
               on Task.TriggerId = Trig.Id
ORDER BY Name desc


//Get Current Batch status of AX to MR integration data sync

USE ManagementReporter

SELECT T.NAME,
CASE TS.STATETYPE
      WHEN 3 THEN 'PROCESSING'
      WHEN 5 THEN 'COMPLETE'
      WHEN 7 THEN 'ERROR'
END AS STATETYPE,
TS.PROGRESS,
TS.LastRunTime as LastRun,
TS.NextRunTime as nextRun,
T.Id as TaskId,
T.TYPEID as TypeId,
T.ID AS TRIGGERID,
CASE TR.ISENABLED
      WHEN 1 THEN 'ENABLED'
      ELSE 'DISABLED'
END AS NAMESTATUS,
TR.INTERVAL,
CASE TR.UNITOFMEASURE
      WHEN 2 THEN 'MINUTES'
      ELSE 'SECONDS'
END AS INTERVALTIMING,
TS.STATETYPE
FROM SCHEDULING.TASK T JOIN SCHEDULING.TASKSTATE TS
ON T.ID = TS.TASKID JOIN SCHEDULING.[TRIGGER] TR ON TR.ID = T.TRIGGERID
WHERE ISENABLED <> 0
AND T.TYPEID in ('55D3F71A-2618-4EAE-9AA6-D48767B974D8',
'6F6B935B-FC0A-46B9-8F53-27C6AF7437F0',
'D81C1197-D486-4FB7-AF8C-078C110893A0')

ORDER BY T.NAME



//Get MR schedule error list

select
b.Name,
c.Text,
DATEADD(minute, DATEDIFF(minute,GETUTCDATE(),GETDATE()), a.StartTime) as LocalStartTime,
DATEADD(minute, DATEDIFF(minute,GETUTCDATE(),GETDATE()), a.EndTime) as LocalEndTime,
a.TotalRetryNumber,
a.IsFailed,
d.Name as TaskType
from [Scheduling].[Log] a
inner join [Scheduling].[Task] b on a.TaskId = b.Id
inner join [Scheduling].[Message] c on a.Id = c.LogId
inner join [Scheduling].[TaskType] d on b.TypeId = d.Id
where a.StartTime > (select DATEADD(DAY, -8, GETDATE()))
and c.Text like '%Error%'
order by LocalStartTime desc

Skip Inventory reservation on Sales order lines in D365FO

[ExtensionOf(classStr(InventUpd_Reservation))] final class InventUpd_ReservationCls_SAN_Extension {     void updateNow()     {         if(mo...