Множественный выбор lookup на диалоге SysOperation Framework в Dynamics 365
Когда вы добавляете поле в контракт данных SysOperation Framework, lookup, который создает платформа (если EDT имеет lookup), представляет собой простой lookup с одним выбором. Давайте посмотрим, как создать множественный выбор в Dynamics 365.
Contents
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!