Когда вы добавляете поле в контракт данных 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 и использовать выбранные значения.
Все комментарии
Чтобы оставить комментарий, необходимо войти или зарегистрироваться.
Пока нет комментариев. Будьте первым!