Множественный выбор lookup на диалоге SysOperation Framework в Dynamics 365

Когда вы добавляете поле в контракт данных SysOperation Framework, lookup, который создает платформа (если EDT имеет lookup), представляет собой простой lookup с одним выбором. Давайте посмотрим, как создать множественный выбор в Dynamics 365.

SysOperation Framework и MVC

Но прежде всего немного введения! Платформа SysOperation Framework была представлена в Dynamics AX 2012 для замены RunBase Framework и используется для создания процессов, которые будут запускаться пакетным сервером. Классы RunBase все еще существуют в Dynamics 365 для финансов и операций. Некоторые стандартные процессы все еще используют его, в то время как другие используют его для последующего вызова класса SysOperations Framework.

Хотя вы все еще можете использовать RunBase Framework, я бы просто работал с SysOperations Framework, так как он работает лучше и использует более современный программный шаблон проектирования MVC (хотя идея шаблона MVC существует с 70-х годов…).

Для этого нам нужно создать класс Data Contract для нашей модели данных, класс Controller, который определит, как выполняется процесс, класс Service с логикой (которая будет классом DataProvider для отчетов) и класс UIBuilder для изменения поведения диалога. В этом примере я перечислю юридических лиц и использую классы SysOperation для запуска отчета.

Data Contract

Класс Data Contract является нашей моделью данных и должен быть помечен атрибутом DataContract. Чтобы создать поиск с множественным выбором, мы объявим List вместо переменной EDT, которая имеет отношение к таблице, которую нам нужно выбрать из нескольких элементов. Метод parm должен быть помечен атрибутами DataMemeber и AifCollectionType.

Наш Data Contract будет выглядеть так:

[
    DataContract,
    SysOperationContractProcessing(classStr(AASSysOpsReportUIBuilder))
]
class AASSysOpsReportDC
{ 
    List selectedEntities;

    [
        DataMember('selectedEntities'),
        SysOperationLabel("@SYS303247"),
        AifCollectionType('return', Types::String)
    ]

    public List parmSelectedLegalEntities(List _selectedEntities = selectedEntities)
    {
        selectedEntities = _selectedEntities;

        return selectedEntities;
    }
}

Платформа SysOperation Framework автоматически создаст пользовательский интерфейс всех членов, помеченных атрибутом DataMember, что является еще одним улучшением по сравнению с RunBase.

Service class (Data Provider)

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

Data Provider

Для отчета мы создаем класс провайдера данных (DP), где заполняем временную таблицу, которая передаёт данные в отчет:

[SRSReportParameterAttribute(classStr(AASSysOpsReportDC))]
class AASSysOpsReportDP extends SrsReportDataProviderPreProcess
{
    AASSysOpsReportTableTmp reportTmp;

    [SRSReportDataSet(tablestr(AASSysOpsReportTableTmp ))]
    public AASSysOpsReportTableTmp getTmpTable()
    {
        select reportTmp;

        return reportTmp;
    }
}

Service

Для класса, выполняющего бизнес-логику, мы делаем следующее, расширяя другой класс:

class AASSysOpsBatchService extends SysOperationServiceBase
{
    AASSysOpsBatchDC contract;

    public void executeOperation(AASSysOpsBatchDC _contract)
    {
        // do things
    }
}

Controller

Контроллер — это место, где мы определяем, как класс будет работать: синхронно или асинхронно, это довольно просто:

class AASSysOpsReportController extends SrsReportRunController
{
    public void execute(Args _args)
    {
        this.parmReportName(ssrsReportStr(AASTestReport, Report));
        this.parmArgs(_args);
        this.startOperation();
    }

    static void main(Args _args)
    {
        new AASSysOpsReportController().execute(_args);
    }
}

Поскольку я запускаю отчет, я не определяю, как будет работать класс. Если бы мы создавали класс Batch, контроллер выглядел бы так (обратите внимание, что в качестве службы он также расширяет другой класс):

class AASBatchClassController extends SysOperationServiceController
{
    public static void main(Args _args)
    {
        AASBatchClassController controller = AASBatchClassController::construct();

        controller.startOperation();
    }

    public static AASBatchClassController construct(SysOperationExecutionMode _executionMode = SysOperationExecutionMode::Asynchronous)
    {
        return new AASBatchClassController(identifierStr(AASSysOpsBatchService), methodStr(AASSysOpsBatchService, executeOperation), _executionMode);
    }
}

Именно здесь мы говорим классу, как запускаться, в этом примере синхронно с параметром _executionMode. Мы можем использовать 4 разных режима:

  • Синхронный (Synchronous): запускает процесс и оставляет пользовательский интерфейс заблокированным до его завершения.
  • Асинхронный (Asynchronous): запускает процесс в пакетном режиме
  • Надежный асинхронный (Reliable asynchronous): запускает процесс в пакетном режиме после нажатия кнопки ОК. Во время работы он будет в списке пакетов. Когда он будет выполнен, он исчезнет.
  • Запланированный пакет (Scheduled batch): то же самое, что и в надежном асинхронном режиме, но задание остается в списке пакетов, когда оно выполнится.

Класс UIBuilder

Класс UIBuilder позволяет нам переопределить, как класс DC отображает некоторые элементы, или улучшить функциональность поля, что мы и хотим сделать:

class AASSysOpsReportUIBuilder extends SrsReportDataContractUIBuilder
{
    AASSysOpsReportDC contract;
    DialogField availableCompanies;

    public void postBuild()
    {
        contract = this.dataContractObject() as AASSysOpsReportDC;
        availableCompanies = this.bindInfo().getDialogField(contract, methodStr(AASSysOpsReportDC, parmSelectedLegalEntities));

        availableCompanies.lookupButton(FormLookupButton::Always);
    }

    public void postRun()
    {
        Query query = new Query();
        QueryBuildDataSource qbdsLegalEntity = query.addDataSourceTable(tablenum(OMLegalEntity));

        qbdsLegalEntity.fields().addField(fieldNum(OMLegalEntity, LegalEntityId));
        qbdsLegalEntity.fields().addField(fieldNum(OMLegalEntity, Name));

        container selectedFields = [tableNum(OMLegalEntity), fieldNum(OMLegalEntity, LegalEntityId)];

        SysLookupMultiSelectCtrl::constructWithQuery(this.dialog().dialogForm().formRun(), availableCompanies.control(), query, false, selectedFields);
    }
}

Мы создаем диалоговое поле и инициализируем его методом parm для нашего List в DC. Затем в методе postRun мы вызываем метод SysLookupMultiSelectCtrl::constructWithQuery. Мы можем либо передать существующий запрос, либо создать его в X++, как я сделал. Обратите внимание, что в этих методах нет вызова super, иначе элементы управления будут созданы SysOperation Framework, и вы получите ошибку во время выполнения.

Результат

Создав все эти классы, мы теперь можем запустить класс Controller и посмотреть, как выглядит lookup.

Записей можно выбирать сколько угодно!

И теперь мы можем получить данные с помощью метода parm в нашем классе Service или Data Provider и использовать выбранные значения.

 

Ссылка на источник

 

Comments

So empty here ... leave a comment!

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

Sidebar