15.02.2018, 19:17 | #301 |
Участник
|
Здравствуйте! Возможно кто-то уже упоминал об этом, но всё же запощу.
Форма, используемая при создании лукапа для мульселект контрола: SysLookupMultiSelectGrid метод executeQuery датасорса Common. X++: if(queryRunCursor) { if(queryRunCursor.isTempDb() || queryRunCursor.isTmp()) { formDataSourceCursor.setTmpData(queryRunCursor); } else { formDataSourceCursor.data(queryRunCursor); } } а вот queryRunCursor.isTempDb() кейс подразумевал бы чего-то вроде: X++: formDataSourceCursor.linkPhysicalTableInstance(queryRunCursor); Скажите пожалуйста что думаете. заранее спасибо. UPD: Dynamics 365 U10 Последний раз редактировалось Cardagant; 15.02.2018 в 19:22. |
|
15.02.2018, 21:39 | #302 |
Участник
|
Цитата:
Сообщение от Cardagant
queryRunCursor.isTmp() кейс покрыт с помощью setTmpData() а вот queryRunCursor.isTempDb() кейс подразумевал бы чего-то вроде: X++: formDataSourceCursor.linkPhysicalTableInstance(queryRunCursor); Скажите пожалуйста что думаете. заранее спасибо. UPD: Dynamics 365 U10 |
|
06.06.2018, 09:08 | #303 |
Участник
|
Улыбнуло - Framework импорта валют в D365
Как его видели создатели судя по описанию -универсальная структура импорта поддерживающая X++ и .NET и все возможные варианты что получилось на самом деле - из 3 источников загрузки(один из которых больше похож на тестовый) один реальный вообще не вписался в эту мега универсальную концепцию, старый добрый If в середине метода |
|
06.06.2018, 09:27 | #304 |
Участник
|
|
|
|
За это сообщение автора поблагодарили: belugin (5). |
06.06.2018, 10:24 | #305 |
Moderator
|
У меня вообще эта концепция "Currency pair" из DAX2012 вызвала подозрение, что этот модуль был спроектирован кем-то без минимального знания бухгалтерского учета. Ну то есть - человек не знал что все равно все валюты нужно через первичную валюту учета пересчитывать, и спроектировал систему, в которой можно держать курсы вообще между любыми двумя валютами и пересчитывать из одной валюты в другую можно напрямую. А потом старшие товарищи указали ему на ошибку, и им пришлось отрубать половину разработанного механизма, чтобы потом в балансе не появились бредовые цифры.
|
|
06.06.2018, 10:40 | #306 |
Участник
|
Цитата:
ну и идею этих классов я не понял, т.е. загрузка курсов это вообще задания не имеющие ничего общего(разные параметры операций, разные параметры запуска, разные правила записи курсов и т.д.), т.е. чем RunBase + статический метод на таблице ExchRate для записи финального результата не устраивал непонятно Последний раз редактировалось trud; 06.06.2018 в 10:50. |
|
06.06.2018, 11:33 | #308 |
Microsoft Dynamics
|
Помню, было очень сложно объяснить авторам фреймворка, что у нас в стране есть курс "на завтра". ЦБ в 12 часов дня публиковал завтрашний курс. Изначально предложенная форма ввода параметров импорта не позволяла учитывать такую особенность. А уж внутренности ребята обещали поправить "в следующей версии", после анализа кода всех провайдеров. Но, видимо, они переключились на другой фронт, и этот фреймворк так и остался как задумался.
|
|
06.06.2018, 12:53 | #309 |
Участник
|
Цитата:
Сообщение от trud
я вот не соглашусь. т.е. если вы разрабатываете с нуля систему классов, вам в первую очередь надо подумать, чтобы другие "не запаривались", т.е. это вообще единственный критерий что вы сделали вашу систему классов правильно - что ее использование упросит разработку а никак не усложнит
ну и идею этих классов я не понял, т.е. загрузка курсов это вообще задания не имеющие ничего общего(разные параметры операций, разные параметры запуска, разные правила записи курсов и т.д.), т.е. чем RunBase + статический метод на таблице ExchRate для записи финального результата не устраивал непонятно Ну и там где гибкость там и сложность, посмотрите что в GER наворотили, там же свой удивительный мир, не похожий на АХ. Последний раз редактировалось skuull; 06.06.2018 в 13:04. |
|
06.06.2018, 13:06 | #310 |
Moderator
|
|
|
04.07.2018, 18:24 | #311 |
Участник
|
Коллега разбирался, почему в D365FO локализаторские отчеты выплевывают Excel-файлы без расширения, по причине чего они кулем валятся в Downloads, и приходится их переименовывать перед открытием. Оказалось, так работает чудесный обновленный XmlExcelReport_RU
|
|
05.07.2018, 12:19 | #312 |
Участник
|
|
|
05.07.2018, 12:35 | #313 |
Участник
|
Цитата:
Коллеги сказали, что ошибка была исправлена. P.S. Вообще, как мне казалось, WTF это для каких-то вопиющих штук типа X++: if (myBoolean.ToString().Length == 5) |
|
05.07.2018, 17:23 | #314 |
Moderator
|
Цитата:
Просто ошибка-то может и не тяжелая, но даже ради самой плевой ошибки приходится преодолевать сопротивление индусов. (Которые, похоже что, последнее время перегружены и письма по кэйзу пишут где-то раз в неделю...) Последний раз редактировалось fed; 05.07.2018 в 18:40. |
|
05.07.2018, 19:53 | #315 |
Участник
|
|
|
06.07.2018, 11:11 | #316 |
Участник
|
Недавно потребовалось в АХ 4.0 просмотреть поля на всех таблицах определенного EDT типа. Соответственно решил использовать метод SysDictField.isDerivedFrom(). В итоге находилось 0 полей. Когда заглянул внутрь, дико ужаснулся. Код работает нормально только для вещественного типа и то как-то через одно место.
X++: /* Returns true if the Extended Data Type property field is derived from the extended data type. Use extendedTypeNum() to get the id of type. */ boolean isDerivedFrom(extendedTypeId id) { Types fieldType; extendedTypeId userTypeId; DictType dictType; boolean retval = false; ; fieldType = this.baseType(); if(fieldType == Types::Real) { dictType = new DictType(this.typeId()); if (this.typeId() == id) retval = true; if(!retval) { userTypeId = this.typeId(); dictType = new DictType(userTypeId); while(dictType!=null && !retval) { if (userTypeId == id) retval = true; else { userTypeId = dictType.extend(); dictType = new DictType(userTypeId); } } } } return retval; } X++: /* Returns true if the Extended Data Type property field is derived from the extended data type. Use extendedTypeNum() to get the id of type. */ boolean isDerivedFrom(extendedTypeId id) { return SysDictType::isEqualOrExtending(this.typeId(), id); }
__________________
// no comments |
|
08.08.2018, 07:47 | #317 |
Мрачный тип
|
Класс EventContextInformation, используемый при переходе со стандартного оповещения к источнику, метод canUseFindRecord(), имеющий встроенную булевскую функцию test().
В строке 50 шикарнейшим образом инициализируется объект QueryBuildDataSource для запроса - тупо добавлением нового источника данных, независимо от того был Query унаследован с переданного источника данных формы или сформирован с нуля (т.е. кол-во источников данных в любом случае увеличивается на 1). Затем этот запрос передается в 92-й строке в SysQuery для подсчета кол-ва записей. Как работает этот подсчет - можете глянуть, при количестве источников данных в запросе большим одного - идет обычный итерационный перебор всех записей на клиенте. Имеющие в таблицах-источниках оповещений значительное кол-во записей скажут спасибо за чудесные минуты ожидание открытия соотв. формы. P.S. И это, <censored>, слой SYP ... X++: /// method returns false. Otherwise, it returns true. /// </remarks> private boolean canUseFindRecord( FormDataSource alertFds = formRun.dataSource(alertFormDsName) ) { tableId tableId,childTableId; List fds2Dynas; ListEnumerator fdsLe; Map dynalinkMap; container dynaPack; MapEnumerator dynaMe; Query q; QueryBuildDataSource qbds; List keyList; Map keyFields; DictIndex di; // If changed remember to updated the Dev. doc! #define.maxRecsForFind(100) boolean test() { // If first then lookup overrules if (alertQbdsNo == 1 && ! this.canUseLookup()) return false; if (! alertBuffer) return false; if(alertFds.query() != null) { q = alertFds.query(); } else { q = new Query(); } qbds = q.addDataSource(alertBuffer.TableId); // Use packed dyna info to find dyna fields from alert buffer to parent if (conlen(packedDynas) > 1) { fds2Dynas = List::create(packedDynas); fdsLe = fds2Dynas.getEnumerator(); fdsLe.moveNext(); [tableId,childTableId,dynaPack] = fdsLe.current(); keyList = new List(Types::Integer); dynalinkMap = Map::create(dynaPack); dynaMe = dynalinkMap.getEnumerator(); while (dynaMe.moveNext()) { keyList.addEnd(dynaMe.currentKey()); } keyFields = SysDictTable::mapFieldIds2Values(keyList,alertBuffer); // Add dyna's values as ranges SysQuery::addRangesFromKeyData(qbds,keyFields.pack()); // Add filter query if (filterQuery) SysQuery::mergeRanges(q,filterQuery); } else { // When no dyna's only use filter if (filterQuery) { q = filterQuery; di = this.getPrimaryIndex(); if (di.field(1) == fieldnum(Common,RecId)) { q.dataSourceTable(alertBuffer.TableId).addRange(di.field(1)).value(SysQuery::value(alertBuffer.(di.field(1)))); } } } if (q && (SysQuery::countLoops(new QueryRun(q)) <= #maxRecsForFind)) { return true; } return false; } if (canUseFindRecordCalled) { return canUseFindRecordValue; } canUseFindRecordValue = test(); canUseFindRecordCalled = true; return canUseFindRecordValue; }
__________________
Мы летаем, кружимся, нагоняем ужасы ... Последний раз редактировалось TasmanianDevil; 08.08.2018 в 08:03. |
|
08.08.2018, 13:37 | #318 |
Мрачный тип
|
Забыл добавить - перебор идет по декартову произведению таблицы-источника самой на себя.
У меня на BatchJobHistory c полутора миллионами записей форма умирала на пяток-другой минут.
__________________
Мы летаем, кружимся, нагоняем ужасы ... |
|
08.08.2018, 13:59 | #319 |
Участник
|
Это какая версия? В Dynamics AX 2012 R3 какой-то CU теперь вот так:
X++: /// <summary> /// Tests whether the <see cref="M:FormDataSource.findRecord" /> method can be used to find the alerted /// record in a form data source. /// </summary> /// <param name="alertFds"> /// Form data source of the alerted buffer; optional. /// </param> /// <returns> /// true if the <c>findRecord</c> method can be used; otherwise, false. /// </returns> /// <remarks> /// If the form data source of the alerted buffer is the first one and <see /// cref="M:EventContextInformation.canUseLookup" /> returns false, then this method returns false. If /// the number of records in the data source is more that 100, this method returns false. Otherwise, it /// returns true. /// </remarks> private boolean canUseFindRecord( FormDataSource alertFds = formRun.dataSource(alertFormDsName) ) { tableId tableId,tableId2,childTableId; List fds2Dynas; ListEnumerator fdsLe; Map dynalinkMap; container dynaPack; MapEnumerator dynaMe; Query q; QueryBuildDataSource qbds; List keyList; Map keyFields; DictIndex di; // If changed remember to updated the Dev. doc! #define.maxRecsForFind(100) boolean test() { // If first then lookup overrules if (alertQbdsNo == 1 && ! this.canUseLookup()) return false; if (! alertBuffer) return false; tableId2 = alertFds.isInheritanceDataSource() && alertFds.isBaseDataSource() ? alertFds.masterInheritanceDataSource().table() : alertFds.table(); q = new Query(); qbds = q.addDataSource(tableId2); // Use packed dyna info to find dyna fields from alert buffer to parent if (conLen(packedDynas) > 1) { fds2Dynas = List::create(packedDynas); fdsLe = fds2Dynas.getEnumerator(); fdsLe.moveNext(); [tableId,childTableId,dynaPack] = fdsLe.current(); keyList = new List(Types::Integer); dynalinkMap = Map::create(dynaPack); dynaMe = dynalinkMap.getEnumerator(); while (dynaMe.moveNext()) { keyList.addEnd(dynaMe.currentKey()); } keyFields = SysDictTable::mapFieldIds2Values(keyList,alertBuffer); // Add dyna's values as ranges SysQuery::addRangesFromKeyData(qbds,keyFields.pack()); // Add filter query if (filterQuery) SysQuery::mergeRanges(q,filterQuery); } else { // When no dyna's only use filter if (filterQuery) { q = filterQuery; di = this.getPrimaryIndex(); if (di.field(1) == fieldNum(Common,RecId)) { q.dataSourceTable(tableId2).addRange(di.field(1)).value(SysQuery::value(alertBuffer.(di.field(1)))); } } } if (q && (SysQuery::countLoops(new QueryRun(q)) <= #maxRecsForFind)) { return true; } return false; } if (canUseFindRecordCalled) { return canUseFindRecordValue; } canUseFindRecordValue = test(); canUseFindRecordCalled = true; return canUseFindRecordValue; }
__________________
Ivanhoe as is.. |
|
08.08.2018, 14:00 | #320 |
Участник
|
|
|
|
За это сообщение автора поблагодарили: trud (1), gl00mie (1). |