Wednesday, February 26, 2020

get class Name of a static method

in different occasions especially while using the SysExtension or SysOperation frameworks I found myself writing again the same code and I wanted something more generic the get the current class name. I didn’t find any built in method so I decided to make this simple helper method:

public static ClassName getClassNameFromFuncName(str _funcName)
{
    ClassName className;
    #define.ObjectMethodSeparator('.')
    #define.StaticMethodSeparator('::')
    
    if (strContains(_funcName, #ObjectMethodSeparator))
        className = any2str(conPeek(str2con(_funcName, #ObjectMethodSeparator), 1));
    
    if (strContains(_funcName, #StaticMethodSeparator))
        className = any2str(conPeek(str2con(_funcName, #StaticMethodSeparator), 1));
    
    if (!className2Id(className))
        throw(error(Error::wrongUseOfFunction(funcName())));
    
    return className;    
}

you can try it:

public static void main(Args _args)
{
    str fullName = funcName();
    
    str className = MYD_Functions::getClassNameFromFuncName(fullName);

    info(className);
}

here is how I used it to make a constructor more generic and reusable in other SysExtension instances:

class EF_ExtFrameworkSample3bisFactory extends EF_ExtFrameworkSample3bisBaseClass
{
}

public static Object construct(str _className, str _mySetting = '')
{
    EF_StringAtttibute attr;
    Object baseCl;
    ClassName factoryClassName;
    ClassId factoryClassId;
    ClassName baseClassName;
    ClassId baseClassId;
        
    factoryClassName = MYD_Functions::getClassNameFromFuncName(funcName());
    factoryClassId = className2Id(factoryClassName);
    baseClassId = SysDictClass::superClass(factoryClassId);
    baseClassName = classId2Name(baseClassId);

    attr = new EF_StringAtttibute(_className);
    baseCl = SysExtensionAppClassFactory::getClassFromSysAttribute(baseClassName, attr);

    if (!baseCl)
    {
    throw error(Error::wrongUseOfFunction(funcName()));
    }

    baseCl.initSetting(_mySetting);

    return baseCl;

}

Monday, February 24, 2020

Checks for duplicate entities that have the same names

there are different forms where we want to perform a check for duplicate entry names.An example is:

\Forms\HcmPersonalContactNew\Methods\checkDuplicateName

which is using this static method:

\Classes\DirUtility\checkDuplicate

the problem with this method is that it works only in a form which has as datasource a DirParty entity. In order to be able to use the same code also in other contexts I slightly modified the code like this:

public static boolean checkDirPartyDuplicate(Common _nameRecord, DirPartyType _partyType, str _entityName)
{
    //Enrico: I adapted the code from \Classes\DirUtility\checkDuplicate to use it also when not in a form
    
    Common common;
    DictTable partyDicttable;

    FormRun formRun;
    Args    args;
    Object  formObject;
    Common  nameRecord;
    FormDataSource  fds;

    args = new Args(formStr(DirPartyVerification));
    args.record(_nameRecord);
    args.parmEnumType(enumNum(DirPartyType));
    args.parmEnum(_partyType);
    args.parm(_entityName);
    formRun = classfactory.formRunClass(args);
    formRun.init();
    formRun.run();
    formRun.wait();


    if (formRun.closedOk() && formHasMethod(formRun, identifierStr(getName)))
    {
        formObject = formRun;
        nameRecord = formObject.getName();

        if (nameRecord.RecId)
        {
            partyDicttable = new SysDictTable(nameRecord.TableId);
            if(partyDicttable)
            {
                common = partyDicttable.makeRecord();
            }
            //we reselect the party since quick create wich uses this code path requires non pessimistic lock selection
            select common where common.RecId == nameRecord.RecId;
            _nameRecord.data(common);
            
        }
    }
    else
    {
        return false;
    }

    return true;
}


now can be used like this:

static void MYD_Functions_checkDirPartyDuplicate(Args _args)
{
    DirPersonName dirPersonName;
    boolean nameChecked = true;
    
    dirPersonName.FirstName = 'enrico';
    dirPersonName.LastName = 'fuchs';
    
    if (DirParameters::find().UseDuplicateCheck == NoYes::Yes
            && DirPersonName::nameLikeCount(dirPersonName.FirstName, dirPersonName.MiddleName, dirPersonName.LastName) > 0)
        {
            nameChecked = MYD_Functions::checkDirPartyDuplicate(dirPersonName, DirPartyType::Person, tableStr(DirPerson));
        }
    
    if (nameChecked)
        info(strFmt('%1', dirPersonName.RecId));
}

Monday, February 17, 2020

Extensions: Extend a macro without overlay - pack/unpack

I have a class that implements SysPackable. When I extend this class I want also to extend the list of variables in the macro.

Note: in Ax 2012 you don’t have SysPackExtensions.
otherwise see here: https://docs.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/extensibility/extend-runbase-class

class EF_SysPackableTrial implements SysPackable
{
    Description description;
    str myString;

    #define.CurrentVersion(1)
    #define.version1(1)
    #localmacro.CurrentList
    description,
    myString
    #endmacro
}

public container pack()
{
    return [#CurrentVersion, #CurrentList];
}

public boolean unpack(container packedClass)
{
    int version     = runbase::getVersion(packedClass);

    switch (version)
    {
        case #CurrentVersion:
            [version,#CurrentList] = packedClass;
            return true;
        default :
            return false;
    }

    return false;
}

public static EF_SysPackableTrial create(container _pack)
{
    EF_SysPackableTrial cl = new EF_SysPackableTrial();

    cl.unpack(_pack);
    return cl;
}

public Description parmDescription(Description _description = description)
{
    description = _description;

    return description;
}

public str parmMyString(str _myString = myString)
{
    myString = _myString;

    return myString;
}

in the extend class I modified the pack and unpack methods

class EF_SysPackableTrialExtend extends EF_SysPackableTrial
{
    Name name;

    #define.CurrentVersion(1)
    #define.version1(1)
    #localmacro.CurrentList1
    name
    #endmacro
}

public container pack()
{
    container ret;
    
    ret = [#CurrentVersion, [#CurrentVersion, #CurrentList], [#CurrentVersion, #CurrentList1]];
    
    return ret;
}

public boolean unpack(container packedClass)
{
    int version = SysOperationHelper::getVersion(packedClass);

    switch (version)
    {
        case #CurrentVersion:
            [version,#CurrentList] = conPeek(packedClass,2);
            [version,#CurrentList1] = conPeek(packedClass,3);
            return true;
        default :
            return false;
    }

    return false;
}

public static EF_SysPackableTrialExtend create(container _pack)
{
    EF_SysPackableTrialExtend cl = new EF_SysPackableTrialExtend();

    cl.unpack(_pack);
    return cl;
}

public Name parmName(Name _name = name)
{
    name = _name;

    return name;
}

and here a simple job to prove it works fine:

static void EF_SysPackableTrialExtend(Args _args)
{
    EF_SysPackableTrialExtend EF_SysPackableTrialExtend, sysPackableTrial1;

    EF_SysPackableTrialExtend = new EF_SysPackableTrialExtend();
    EF_SysPackableTrialExtend.parmDescription('ciao1');
    EF_SysPackableTrialExtend.parmMyString('ciao2');
    EF_SysPackableTrialExtend.parmName('pp');

    sysPackableTrial1 = EF_SysPackableTrialExtend::create(EF_SysPackableTrialExtend.pack());

    info(sysPackableTrial1.parmDescription());
    info(sysPackableTrial1.parmMyString());
    info(sysPackableTrial1.parmName());
}

I posted this sample also on the forum https://community.dynamics.com/ax/f/microsoft-dynamics-ax-forum/380610/extensions-extend-a-macro-without-overlay---pack-unpack-in-ax-2012

Thursday, February 13, 2020

packed class and Table InMemory Serializer run on server

if you look this class \Classes\HcmPositionCopy follow a pattern to run on server the heavier process.

when I tried to make a similar code for my code I had a problem with an inMemory temp table I wanted to pass as parameter. Here my code how I solved using SysTableInMemorySerializer class.

image

class EF_ClassPack_Trial1
{
    //same template as \Classes\HcmPositionCopy
    
    //input parameters
    Name name;
    EF_ClassPackTmp_Trial1 tmp;
    PackedTable packedTmp;
    
    //output parameters
    Description descr;
    
    ParametersX packedClass;
    
    #define.CurrentVersion(3)
    #localmacro.CurrentList
    
    name,
    tmp,
    descr,
    packedTmp
    
    #endmacro
}

protected static EF_ClassPack_Trial1 construct()
{
    return new EF_ClassPack_Trial1();
}

public static EF_ClassPack_Trial1 newTrial()
{
    EF_ClassPack_Trial1 cl;
    
    cl = EF_ClassPack_Trial1::construct();
    
    return cl;
}

public Name parmName(Name _name = name)
{
    name = _name;

    return name;
}

public EF_ClassPackTmp_Trial1 parmTmp(EF_ClassPackTmp_Trial1 _tmp = tmp)
{
    EF_ClassPackTmp_Trial1 tmp1;
    
    if (prmisDefault(_tmp))
        SysTableInMemorySerializer::unpackTable(packedTmp, tmp1); 
    else    
        packedTmp = SysTableInMemorySerializer::packTable(_tmp);
            
    return tmp1;    
}

public Description parmDescr(Description _descr = descr)
{
    descr = _descr;

    return descr;
}

public void run()
{
    startLengthyOperation();
    
    packedClass = EF_ClassPack_Trial1::runOnServer(this.pack());
    
    endLengthyOperation();
}

public client server static EF_ClassPack_Trial1 create(container _packedClass)
{
    EF_ClassPack_Trial1 cl = EF_ClassPack_Trial1::construct();

    cl.unpack(_packedClass);

    return cl;
}

public container pack()
{
    return [#CurrentVersion, #CurrentList];
}

public boolean unpack(container _packedClass)
{
    Integer version = conPeek(_packedClass,1);

    switch (version)
    {
        case #CurrentVersion:
            [version, #CurrentList] = _packedClass;
            break;
        default:
            return false;
    }

    return true;
}

public EF_ClassPack_Trial1 getRunOutput()
{
    return EF_ClassPack_Trial1::create(packedClass); 
}

public static server container runOnServer(container _packedClass)
{
    EF_ClassPack_Trial1 cl = EF_ClassPack_Trial1::create(_packedClass);
    cl.doSomeOperation();
    
    return cl.pack();
}

private void doSomeOperation()
{
    Counter tmpCount;
    
    tmp = this.parmTmp();
    
    select count(RecId) from tmp;
    tmpCount = int642int(tmp.RecId);
    
    descr = strFmt('ciao %1 - tot: %2', name, tmpCount);    
}

public static void main(Args _args)
{
    EF_ClassPack_Trial1 cl;
    //EF_ClassPack_Trial1 clOptput;
    EF_ClassPackTmp_Trial1 tmp;
    
    tmp.Field1 = 'ciao';
    tmp.insert();
    
    tmp.Field1 = 'ciao1';
    tmp.insert();
    
    cl = EF_ClassPack_Trial1::newTrial();
    cl.parmName('Giorgio');
    cl.parmTmp(tmp);
    cl.run();
    
    //clOptput = cl.getRunOutput();
    //info(clOptput.parmDescr());
    
    info(cl.getRunOutput().parmDescr());
}

Tuesday, January 21, 2020

Date effective child form in Dynamics 2012

in a form to display the date effective filter pane you have to add this snippet in the form init method:

DateEffectivenessPaneController::constructWithForm(
        this,
        MYD_ClinicianPositions_ds);

the DateEffectivenessPaneController is responsible of creating all the user interface and the interaction.

image

if you also have a child form which is also date effective and you want it also to be filtered based on the date effective selection on the parent form you can do this:

public class FormRun extends ObjectRun
{
    SysFormSplitter_X verticalSplitter;
    DateEffectivenessPaneController effectivenessPaneController;
}

public void init()
{
    super();
    //Initialize splitter
    verticalSplitter = new SysFormSplitter_X(VSplitter, GridContainer, element, 300);

    //initialize the DateEffectivenessPaneController
    effectivenessPaneController = DateEffectivenessPaneController::constructWithForm(this, MYD_Mentor_ds);
}

then override the executeQuery method of the subform darasource:

public void executeQuery()
{
    FromDate fromDate = dateNull();
    ToDate toDate = dateMax();
    
    TransDate showAsOfDate;
    FormCheckBoxControl showAllCheckbox;
    boolean showAll;
    
    //get the selection from the parent form date effective toolbar
    showAsOfDate = effectivenessPaneController.parmShowAsOfDate();
    showAllCheckbox =  effectivenessPaneController.parmShowAllCheckbox();
    showAll = showAllCheckbox.value();
    
    if (showAll)
        this.query().validTimeStateDateRange(fromDate, toDate);
        
    if (showAsOfDate)
        this.query().validTimeStateAsOfDate(showAsOfDate);

    super();
}