RunBase Framework в MS DAX 2012

Платформа RunBase обеспечивает стандартизированный подход к созданию процессов и пакетных заданий в Microsoft Dynamics AX. Фреймворк должен использоваться для каждой функции наподобие задания в приложении.

Описание

Среда RunBase использует инфраструктуру Dialog для запроса ввода данных (параметров) пользователем. Параметры являются переменными-членами класса и добавляются в диалоговое окно. Тип элемента управления, который используется в диалоговом окне, зависит от расширенного типа данных, который используется при добавлении элементов управления в диалоговое окно. Класс также использует платформу SysLastValue для сохранения данных об использовании и платформу Operation Progress для отображения хода выполнения операции.

Каждый диалоговый элемент управления имеет соответствующую переменную класса, которая используется для установки и получения значений из диалоговых элементов управления и для сохранения значений для использования позже, когда задание выполняется пакетным сервером.

RunBase

Предположим, что необходимо получить список заказов на продажу определенного клиента за некоторый промежуток времени и произвести над ним какие-либо действия.

classDeclaration

Переменные класса добавляются в локальный макрос в виде «списка» переменных, чтобы их значения могли сохраняться средой SysLastValue. Список переменных имеет соответствующий номер версии для отслеживания различных версий списков, которые могут существовать. Номер версии обычно начинается с единицы и увеличивается каждый раз, когда изменяется число переменных в списке. Метод распаковки должен быть соответствующим образом изменен, чтобы обеспечить правильное назначение значений переменным и предотвратить возникновение ошибок трассировки стека, когда размер постоянных переменных не соответствует размеру локального списка макросов.

class RunBase_Example extends RunBase
{
    CustAccount     custAccount;
    FromDate        fromDate;
    ToDate          toDate;
	 
    DialogField     dfCustAccount;
    DialogField     dfFromDate;
    DialogField     dfToDate;
	 
    #define.CurrentVersion(1)	//Текущая версия данных
    #define.Version1(1)		//Первая версия
    #localmacro.CurrentList 	//Список переменных
	custAccount,
	fromDate,
	toDate
    #endmacro
}

dialog

Вызывается из метода prompt() и возвращает класс запуска формы dialog, formRun. Элементы управления добавляются в диалоговое окно, а значения элементов управления задаются значениями соответствующих им переменных класса.

protected Object dialog()
{
    DialogRunbase dialog;    
 
    dialog              = super();

    dfCustAccount 	= dialog.addFieldValue(typeId(CustAccount), custAccount);
    dfFromDate		= dialog.addFieldValue(typeId(FromDate), fromDate);
    dfToDate		= dialog.addFieldValue(typeId(ToDate), toDate);

    return dialog;
}

getFromDialog

Значения переменных класса назначаются значениями элементов управления, которые были добавлены в диалоговое окно.

public boolean getFromDialog()
{
    boolean ret;    
				 
    ret       	= super();

    custAccount = dfCustAccount.value();
    fromDate   	= dfFromDate.value();
    toDate   	= dfToDate.value();
 
    return ret;
}

initParmDefault

Вызывается, когда метод unPack возвращает false, т.е. данные об использовании не найдены. Переменные класса, которые находятся в списке макросов, должны быть инициализированы в методе initParmDefault.

public void initParmDefault()
{
    super();

    fromDate 	= systemDateGet();
    toDate 	= systemDateGet();
}

new

Создает новый экземпляр класса. Вызывается при выполнении в пакете. Всегда вызывайте super() в новом методе. Новый метод не должен быть слишком медленным (он периодически вызывается для административных целей). Поскольку классы, наследуемые от RunBase, могут выполняться в журнале обработки пакетов, новый метод не должен иметь никаких аргументов. Причина этого заключается в том, что типы переменных объекта могут вызывать ошибки трассировки стека, когда система перечисляет все дочерние классы RunBase в процессе настройки пакетных журналов.

public void new()
{
    super();
}

pack

Переменные класса сохраняются с помощью локального макроса. Вызывается, когда диалоговое окно закрывается нажатием «ОК».

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

run

Основной метод класса. Так же вызывается при пакетной обрабоке.

Если задание выполняет операцию, которая занимает значительное время, рекомендуется отобразить диалоговое окно хода выполнения. Платформа RunBase использует платформу Operation Progress для отображения хода выполнения операции. Чтобы отобразить текущий прогресс, необходимо добавить код в метод run() класса.

public void run()
{
    #OCCRetryCount
    if (! this.validate())
        throw error("");

    try
    {
        this.progressInit();
        ttsBegin;
        // Обнуление переменных, измененных в транзакции
        ...
        // Выполнение задания
        while (queryRun.next())
        {
            progress.incCount();
            progress.setText();

            salesTable = queryRun.get(tableNum(SalesTable));
            ...
            ...
        ttsCommit;
    }
    catch (Exception::Deadlock)
    {
        retry;
    }
    catch (Exception::UpdateConflict)
    {
        if (appl.ttsLevel() == 0)
        {
            if (xSession::currentRetryCount() >= #RetryNum)
            {
                throw Exception::UpdateConflictNotRecovered;
            }
            else
            {
                retry;
            }
        }
        else
        {
            throw Exception::UpdateConflict;
        }
    }
}      

unpack

Переменные класса в локальном макросе восстанавливаются. Метод распаковки вызывается перед запуском. Так же вызывается при выполнении в пакете.

public boolean unpack(container _packedClass)
{
    Version version = RunBase::getVersion(_packedClass);    
 
    switch (version)
    {
        case #CurrentVersion:
            [version, #CurrentList] = _packedClass;
            break;
        default:
            return false;
    }
 
    return true;
}

description

Возвращает описание (базового) класса и будет отображаться как описание в списке пакетов, и определяет заголовок диалогового окна. Метод описания должен иметь свойство RunAs, установленное в Called или эквивалентное поведение. Если класс определен как клиент или сервер, нужно определить метод как клиент-сервер.

public static ClassDescription description()
{
    return "Description";
}

construct

public static RunBase_Example construct()
{
    return new RunBase_Example();    
}

main

По умолчанию класс не является «исполняемым», то есть его нельзя открыть из главного меню или из формы с использованием пункта меню. Чтобы сделать класс «исполняемым», он должен иметь статический метод main (точку входа) с одним параметром Args. Когда класс выполняется, ядро вызывает статический метод main класса и передает ему объект Args. Основной метод вызывается интерактивно и не вызывается пакетным сервером. Объект Args содержит свойства, которые могут использоваться классом. С помощью пункта меню доступ к классу (заданию) можно получить из главного меню или из формы. Важно всегда устанавливать ключ безопасности новых пунктов меню.

public static void main(Args _args)
{
    RunBase_Example runBase = RunBase_Example::construct();    

    if (runBase.prompt())
    {
        runBase.run();
    }
}

RunBaseBatch

Класс RunBaseBatch расширяет класс RunBase и позволяет создавать классы (задания), которые могут быть добавлены в очередь пакета. Как только задание будет добавлено в очередь, оно будет выполнено пакетным сервером. Эта концепция известна как пакетная обработка.

Внимание! Чтение и запись файлов обычно происходит на уровне клиента. Поскольку задание выполняется пакетным сервером, используемый путь должен быть доступен для пакетного сервера или AOS в зависимости от того, на каком уровне выполняется задание. Ошибки трассировки стека будут возникать, если используется путь на уровне клиента, поскольку пакетный сервер и AOS не могут получить доступ к файловой системе клиента.

canGoBatch

Возвращает true если возможна пакетная обработка (т.е. можно указать когда запускать обработку и как часто ее запускать в автоматическом режиме). Возвращает false если пакетная обработка не возможна. Указывает, может ли класс выполняться в пакетном режиме. Каркас добавляет вкладку «Пакет» в диалоговое окно, если этот метод возвращает true. По умолчанию возвращает true.

            
public boolean canGoBatch()
{
    return true;	
}

showQueryValues

Класс RunBaseBatch поддерживает использование запроса для выбора базовых данных. Если класс использует запрос, а метод queryRun переопределяется, среда RunBase автоматически отображает диапазоны запросов и их значения в диалоговом окне. Нажав кнопку «Выбрать», пользователь может изменить запрос, установив или удалив значения запроса или добавив новые диапазоны. Можно расширить запрос, добавив отношения (объединения) к связанным таблицам и, наконец, изменив порядок сортировки.

public boolean showQueryValues()
{
    return true;    
}

queryRun

Метод возвращает QueryRun.

public SysQueryRun queryRun()
{
    return queryRun;
}

initQuery

В методе initQuery() происходит инициализация запроса.

private Query initQuery()
{
    Query           query = super();
    QueryBuildRange qbr;    

    qbr = query.dataSourceTable(tableNum(SalesTable)).findRange(fieldNum(SalesTable, CustAccount));
    if (!qbr)
    {
        qbr = query.dataSourceTable(tableNum(SalesTable)).addRange(fieldNum(SalesTable, CustAccount));
    }
    qbr.value(custAccount);

    qbr = query.dataSourceTable(tableNum(SalesTable)).findRange(fieldNum(SalesTable, SalesId));
    if (!qbr)
    {
        qbr = query.dataSourceTable(tableNum(SalesTable)).addRange(fieldNum(SalesTable, SalesId));
    }
    qbr.value(salesIdFilter);

    qbr = query.dataSourceTable(tableNum(SalesTable)).findRange(fieldNum(SalesTable, ShippingDateRequested));
    if (!qbr)
    {
        qbr = query.dataSourceTable(tableNum(SalesTable)).addRange(fieldNum(SalesTable, ShippingDateRequested));
    }
    qbr.value(SysQuery::range(fromDate, toDate));

    return query;
}

initQueryRun

public void initQueryRun()
{    
    queryRun = new QueryRun(this.initQuery());
}

getFromDialog

public boolean getFromDialog()
{
    boolean ret;    
                 
    ret         = super();

    custAccount = dfCustAccount.value();
    fromDate    = dfFromDate.value();
    toDate      = dfToDate.value();
 
    this.initQueryRun();    // Добавим инициализацию queryRun

    return ret;
}

RunBaseReport

Класс RunBaseReport является частью RunBase Framework. Его основная цель — предоставить пользовательский интерфейс до запуска отчета и упаковать бизнес-логику. Класс RunBaseReport решает проблему отсутствия наследования для отчетов в Dynamics AX. Но Dynamics AX поддерживает наследование для классов.

lastValueElementName

Метод LastValueElementName() возвращает имя отчета, который должен быть запущен. Фреймворк использует запрос из отчета

public identifiername lastValueElementName() 
{ 
    return reportstr(MyReport);
} 

pack

Переопределение метода pack().

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

unpack

Переопределение метода unpack().

public boolean unpack(container packedClass)
{
    Version     version = RunBase::getVersion(packedClass);
    container   base;
    boolean     ret;

    switch (version)
    {
        case #CurrentVersion:
            [version, #CurrentList, base] = packedClass;
            ret = super(base);
            break;
        default:
            ret = false;
            break;
    }

    return ret;
}

Перекрытие методов полей диалога

Предположим, что на форму дилога добавили два поля: useFilter(checkBox) и salesIdFilter(string).

Далее понадобятся названия добавленных элементов. Для того, чтобы узнать имя элемента управления, необходимо запустить диалог и в контекстном меню необходимиого элемента выберать параметры. Но рекомендуется при добавлении полей задавать им произвольное имя.

Внимание! После добавления новых переменных в список #CurrentList необходимо изменить текущую версию.

class RunBase_Example extends RunBase
{
    CustAccount     custAccount;
    TransDate       fromDate;
    TransDate       toDate;
    // -->
    NoYes           useFilter;
    SalesIdFilter   salesIdFilter;  //Новый расширенный тип данных
    // <-- 
    DialogField dfCustAccount; 
    DialogField dfFromDate; 
    DialogField dfToDate; 
    // -->
    DialogField     dfUseFilter;
    DialogField     dfSalesIdFilter;   

    DialogRunbase   dialog; 
    // <-- 
    #define.CurrentVersion(2) //Текущая версия данных 
    #define.Version1(1) //Первая версия 
    #localmacro.CurrentList //Список переменных 
        custAccount,  
        fromDate, 
        toDate, 
        // -->
        useFilter,
        salesIdFilter
        // <--
    #endmacro
}

Далее необходимо добавить поля в диалог.

protected void dialog()
{	
    ...
    // -->	
    dialog.customFieldName('dfUseFilter');
    dfUseFilter = dialog.addFieldValue(typeId(NoYes), useFilter, 'Фильтр по номеру заказа');

    dialog.customFieldName('dfSalesIdFilter');
    dfSalesIdFilter = dialog.addFieldValue(typeId(SalesIdFilter), salesIdFilter);

    dfSalesIdFilter.enabled(dfUseFilter.value() == NoYes::Yes);  //Отключаем поле по умолчанию
    dfSalesIdFilter.replaceOnLookup(false);	//Выключает замещение текста, так чтобы можно было выбирать несколько значений

    dialog.allowControlMethodOverload(true);
    // <--
}

Чтобы получить введенное пользователем значение, изменим метод getFromDialog()

public boolean getFromDialog()
{
    boolean ret;
    ;
                 
    ret         = super();

    custAccount = dfCustAccount.value();
    fromDate    = dfFromDate.value();
    toDate      = dfToDate.value();
    // -->
    useFilter     = dfUseFilter.value();
    salesIdFilter = dfSalesIdFilter.value();
    // <--    
 
    return ret;
}

lookup

Ниже представлен один из вариантов использования перекрытия метода lookup() элемента управления.

protected void dfSalesIdFilter_lookup()
{
    SysTableLookup       sysTableLookup       = SysTableLookup::newParameters(tableNum(SalesTable), dfSalesIdFilter.control());
    Query                query                = new Query();
    QueryBuildDatasource queryBuildDatasource = query.addDataSource(tableNum(SalesTable));
    ;

    dialog.dialogOnClient().updateServer();

    if (custAccount)
    {
        queryBuildDatasource.addRange(fieldNum(SalesTable, OrderAccount)).value(custAccount);
    }

    sysTableLookup.addLookupfield(fieldNum(SalesTable, SalesId));
    sysTableLookup.addLookupfield(fieldNum(SalesTable, CustAccount));
    sysTableLookup.addLookupfield(fieldNum(SalesTable, ShippingDateRequested));

    sysTableLookup.parmQuery(query);

    sysTableLookup.performFormLookup();
}

modified

Ниже представлен один из вариантов использования перекрытия метода modified() элемента управления.

protected boolean dialogMachineryDeploy_modified()
{
    boolean ret = dfUseFilter.control().modified();
    ;

    dialog.dialogOnClient().updateServer();

    dfSalesIdFilter.enabled(dfUseFilter.value() == NoYes::Yes);
    dfSalesIdFilter.value('');

    dialog.updateClient(dialog.dialogOnClient());

    return ret;
}

Примечания

  1. RunBaseBatch
  2. RunBaseReport

См. также

 

Comments

So empty here ... leave a comment!

Добавить комментарий

Sidebar