Change Tracking Usage

Runnable class to install/re-install/disable change tracking scopes
VKChangeTrackingSetupMode  enum:

class VKChangeTrackingSetup extends RunBase
{
    VKChangeTrackingSetupMode  setupMode;

    DialogField                 setupModeFld;

    #define.CurrentVersion(1)
    #localMacro.CurrentList
        setupMode
    #endmacro

}
public void run()
{
    new AifChangeTrackingPermission().assert();

    info(strFmt("Change tracking setup mode '%1'", enum2str(setupMode)));

    this.setupDbChangeTracking();


    // Add here the methods to enable change tracking for your queries
    this.setupMagentoProducts();
    this.setupMDAProducts();


    // Make sure that the batch job which updates change trackng version is scheduled
    ttsBegin;
    AifChangeTrackingVersionUpdateJob::createBatchJob();
    ttsCommit;


    CodeAccessPermission::revertAssert();
}
protected Object dialog()
{
    DialogRunbase   dialog;
    DialogGroup     dialogGroup;

    dialog = super();

    dialog.caption("Setup change tracking scopes");

    dialogGroup = dialog.addGroup();
    DialogGroup.columns(2);
    dialogGroup.frameType(FormFrameType::Thin);
    dialog.addImage(931);
    dialog.addText("The option 'Install all scopes' will disable existing scopes before re-installing them. This will discard any previously tracked versions.", 500);

    dialogGroup = dialog.addGroup();
    setupModeFld = dialog.addFieldValue(enumStr(VKChangeTrackingSetupMode), setupMode);

    return dialog;
}
protected void disableQueryChangeTracking(AifChangeTrackingScope  _scope)
{
    AifChangeTrackingConfiguration::disableChangeTracking(_scope);
    AifChangeTrackingConfiguration::deleteTouchTriggers(_scope);
}
protected void enableQueryChangeTracking( Query                   _query,
                                        AifChangeTrackingScope  _scope,
                                        boolean                 _createInsertTriggers = true,
                                        boolean                 _createUpdateTriggers = true,
                                        boolean                 _createDeleteTriggers = true)
{
    AifChangeTrackingConfiguration::enableChangeTrackingForQuery(   _scope, _query,
                                                                    AifChangeTrackingType::SqlChangeTracking,
                                                                    _createDeleteTriggers);

    if (_createInsertTriggers)
        AifChangeTrackingConfiguration::createTouchTriggersForQuery(_scope, AifChangeTrackingTriggerType::AfterInsert, _query);

    if (_createUpdateTriggers)
        AifChangeTrackingConfiguration::createTouchTriggersForQuery(_scope, AifChangeTrackingTriggerType::AfterUpdate, _query);
}
public boolean getFromDialog()
{
    boolean ret;

    ret = super();

    setupMode = setupModeFld.value();

    return ret;
}
public container pack()
{
    return [#CurrentVersion, #CurrentList];
}
protected void scheduleCtVersionUpdates()
{
    if (setupMode != VKChangeTrackingSetupMode::Disable)
        AifChangeTrackingVersionUpdateJob::createBatchJob();
}
protected void setupDbChangeTracking()
{
    if (setupMode != VKChangeTrackingSetupMode::Disable
        &&  !AifChangeTrackingConfiguration::isChangeTrackingEnabled())
    {
        AifChangeTrackingConfiguration::enableChangeTracking(true);
    }

    info(AifChangeTrackingConfiguration::isChangeTrackingEnabled() ? "Database change tracking is enabled" : "Database change tracking is disabled");
}
private void setupMagentoProducts()
{
    this.setupQueryChangeTracking(queryStr(MagentoProductChangeTracking), 'MagentoProducts');
}
protected void setupMDAProducts()
{
    this.setupQueryChangeTracking(queryStr(MDAProductInfoChangeTracking), 'MDAProducts');
}
protected void setupQueryChangeTracking(  str                     _queryName,
                                        AifChangeTrackingScope  _scope,
                                        boolean                 _createInsertTriggers = true,
                                        boolean                 _createUpdateTriggers = true,
                                        boolean                 _createDeleteTriggers = true)
{
    Query                   query;
    AifChangeTracking       changeTracking;
    boolean                 isEnabled;

    setPrefix(strFmt("Change tracking scope '%1'", _scope));


    query = new Query(_queryName);

    changeTracking  = AifChangeTracking::construct(query, _scope, AifChangeTrackingType::SqlChangeTracking);
    isEnabled       = changeTracking.isChangeTrackingEnabledForQuery();

    switch (setupMode)
    {
        case VKChangeTrackingSetupMode::InstallNew:
            if (!isEnabled)
                this.enableQueryChangeTracking(query, _scope, _createInsertTriggers, _createUpdateTriggers, _createDeleteTriggers);
            break;

        case VKChangeTrackingSetupMode::InstallAll:
            if (isEnabled)
                this.disableQueryChangeTracking(_scope);
            this.enableQueryChangeTracking(query, _scope, _createInsertTriggers, _createUpdateTriggers, _createDeleteTriggers);
            break;

        case VKChangeTrackingSetupMode::Disable:
            if (isEnabled)
                this.disableQueryChangeTracking(_scope);
    }

    info(changeTracking.isChangeTrackingEnabledForQuery()? "Change tracking for query is enabled" : "Change tracking for query is disabled");
}
public boolean unpack(container packedClass)
{
    Version version = RunBase::getVersion(packedClass);

    switch (version)
    {
        case #CurrentVersion:
            [version, #CurrentList] = packedClass;
            break;

        default:
            return false;
    }

    return true;
}
public static VKChangeTrackingSetup construct()
{
    return new VKChangeTrackingSetup();
}
public static void Main(Args _args)
{
    VKChangeTrackingSetup::MainOnServer(_args);
}
private server static void MainOnServer(Args _arhs)
{
    VKChangeTrackingSetup  changeTrackingSetup;

    changeTrackingSetup = VKChangeTrackingSetup::construct();
    if (changeTrackingSetup.prompt())
    {
        changeTrackingSetup.run();
    }
}
MagentoProductChangeTracking, MDAProductInfoChangeTracking - queries in AOT with exact fields list. Changes in the stated datasources and its field list will be tracked.

Helper class to get changed entities:
class VKChangeTrackingHelper
{
    AifChangeTracking       changeTracking;
    AifChangeTrackingScope  scope;
    UnknownNoYes            fullSyncRequired;
}
public AifChangeTrackingTable getChanges()
{
    AifChangeTrackingTable  changeTrackingTable;
    utcDateTime             lastSyncDate;

    lastSyncDate    = VKChangeTrackingScope::getLastSyncDate(scope);
    changeTracking.getChanges(lastSyncDate, changeTrackingTable);

    return changeTrackingTable;
}
public boolean isFullSyncRequired()
{
    utcDateTime             lastSyncDate;


    if (fullSyncRequired == UnknownNoYes::Unknown)
    {
        lastSyncDate = VKChangeTrackingScope::getLastSyncDate(scope);
        if (changeTracking.isFullSynchronizationRequired(lastSyncDate))
            fullSyncRequired = UnknownNoYes::Yes;
        else
            fullSyncRequired = UnknownNoYes::No;
    }

    return fullSyncRequired == UnknownNoYes::Yes;
}
public void new(Query _query, AifChangeTrackingScope _scope)
{
    scope           = _scope;
    changeTracking  = AifChangeTracking::construct(_query);
}
Table to save last sync date:
Name: VKChangeTrackingScope
Fields:
  • LastSyncDate, EDT: DateTimeExecuted
  • Scope, EDT: AifChangeTrackingScope, unique
public static boolean exist(AifChangeTrackingScope _scope)
{
    return _scope && (
        select RecId from VKChangeTrackingScope
        where   VKChangeTrackingScope.Scope        == _scope).RecId != 0;
}
public static VKChangeTrackingScope find(AifChangeTrackingScope _scope, boolean _forUpdate = false)
{
    VKChangeTrackingScope  changeTrackingScope;

    changeTrackingScope.selectForUpdate(_forUpdate);

    if (_scope)
    {
        select firstOnly changeTrackingScope
        where   changeTrackingScope.Scope == _scope;
    }

    return changeTrackingScope;
}
public static utcDateTime getLastSyncDate(AifChangeTrackingScope _scope)
{
    VKChangeTrackingScope  changeTrackingScope;

    changeTrackingScope = VKChangeTrackingScope::find(_scope);

    return changeTrackingScope.LastSyncDate;
}
public static void updateLastSyncDate(  AifChangeTrackingScope  _scope,
                                        utcDateTime             _dateTime = DateTimeUtil::getSystemDateTime())
{
    VKChangeTrackingScope  changeTrackingScope;

    ttsBegin;
    changeTrackingScope = VKChangeTrackingScope::find(_scope, true);

    if (!changeTrackingScope)
    {
        changeTrackingScope.Scope = _scope;
    }

    changeTrackingScope.LastSyncDate = _dateTime;
    changeTrackingScope.write();
    ttsCommit;
}

Usage

Modify initial query to add Aif change tracking table:
    if (    exportMode == VKInterfaceExportMode::Delta
        &&  !changeTrackingHelper.isFullSyncRequired())
    {
        qbds = qbds.addDataSource(tableNum(AifChangeTrackingTable));
        // should be joined to primary datasource of change tracking query. In this case we are tracking changes on the InventTable
        qbds.addRange(fieldNum(AifChangeTrackingTable, RecId))
            .value(strFmt('(%1.RecId == %2.KeyField_RecId)', qbdsInventTable.name(), qbds.name()));
    }
AifChangeTrackingTable is temporary table and is filled in by helper class. The instance of this temp table should be added to QueryRun:

    queryRun = new QueryRun(query);

    if (    exportMode == VKInterfaceExportMode::Delta
        &&  !changeTrackingHelper.isFullSyncRequired())
    {
        queryRun.setRecord(changeTrackingHelper.getChanges());
    }

    while (queryRun.next())
    {
        //....
    }
by Aris Xanthopoulos

Search

About

DaxOnline.org is free platform that allows you to quickly store and reuse snippets, notes, articles related to Dynamics AX.

Authors are allowed to set their own AdSense units.
Join us.

Blog Tags