| 
			
			 | 
		#1 | 
| 
			
			 NavAx 
		
			
	 | 
	
	
	
		
		
			
			 
			
			Проверялось на AX 2009, RU5, RU6, RU7 
		
		
		
			Существует проблема, при которой Аксапта 2009 не возвращает существование mapping'а для какой-либо таблицы, входящей в map. Это проявляется только в пакетном режиме на сервере (новый режим, появившийся в 2009). При просто выполнении на сервере, такого не происходит. Подробности: Есть некий код: X++:     treeNode = infolog.getNode(UtilElementType::TableMap,
                               tableid2name(tablenum(VendInvoiceTrans)),
                               tablenum(CustVendInvoiceTrans));Это не было бы так неприятно, если бы вышеприведенный код не был почти 1:1, использующимся в методе Global::mappingExists_RU, из-за чего данный метод при исполнении в пакетном режиме, никогда не возвращает true. Но и это - не самая крупная проблема. ![]() Настоящие проблемы начинаются, когда при пакетной разноске накладных в методе CustVendInvoiceTrans.initFromTaxWorkTrans_RU в результате некорректной работы метода mappingExists_RU, не заполняются поля TaxAmountMST_W (Сумма налога в валюте), vatAmountMST_RU (Сумма НДС), ExciseAmountMST_RU (Сумма акциза) в строках накладных, и далее, ессно, в строках счетов-фактур по этим накладным. В результате, когда формируем книгу покупок/продаж по этим с/ф, возникают еще одни проблемы. В прилагаемом классе воспроизведена подобная ситуация. Желающим проверить могу посоветовать запустить его интерактивно, и в пакете, и посмотреть на разницу в выдаваемых сообщениях. Варианты решения разнообразны, думаю, не стоит их приводить. Еще советовал бы проверить уже существующие обработки, работающие в пакетном режиме, на использование данных методов. 
				__________________ 
		
		
		
		
	Жизнь прекрасна! Если, конечно, правильно подобрать антидепрессанты...  
			 | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: mazzy (5), db (5), lev (3). | |
| 
			
			 | 
		#2 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Во-первых, зачем вам тормозной TreeNode, когда можно воспользоваться шустрыми UtilElements'ами? Во-вторых, по-моему, никогда не стоит создавать TreeNode'ы с помощью указанного метода infolog несмотря на все его кажущиеся удобства - он ведь будет создавать их там, где сам работает, т.е. всегда на клиенте. В-третьих, когда код работает в пакетном режиме на сервере, кое-какие объекты, обычно живущие на клиенте, могут оказаться неинициализированными - весьма вероятно, оттого вы и получаете null вместо TreeNode. В общем, попробуйте искать Mapping через UtilElements - оно и шустрее, и надежней. 
		
		
		
		
		
		
		
		
			А на счет локализаторского кода - я лично в свое время огреб очень много проблем, когда пытался использовать в серверном коде класс MappingsInfo_RU. Код работал строго на сервере, дергался часто, но при этом откуда ни возьмись возникал офигенный клиент-серверный трафик и сопутствующие тормоза. Дело оказалось как раз в том, что в этом классе TreeNode для Mapping'а получался с помощью этого метода infolog - на клиенте. Затем на сервере создавался TreeNodeIterator и шерстил дочерние узлы этого клиентского TreeNode'а со всеми вытекающими последствиями с т.з. трафика и производительности. В общем, глаз да глаз нужен за локализаторским кодом ![]() PS. Ломанулся было исправлять mappingExists_RU(), но тут понял, что он в моем случае работает корректно, потому что используемый им MappingsInfo_RU давно исправлен. Уф...  
		Последний раз редактировалось gl00mie; 02.06.2011 в 00:52.  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: lev (2). | |
| 
			
			 | 
		#3 | 
| 
			
			 NavAx 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Во-первых, вообще-то разговор о том, что это - стандартный  функционал. 
		
		
		
		
		
		
			Т.е. разносишь накладные в пакете - получаешь проблему. Во-вторых, я бы хотел посмотреть, как при исполнении в пакете, TreeNode создастся на клиенте. ![]() В-третьих, что там "должно использоваться" - это уже вопрос к восточноевропейским локализаторам  
		
				__________________ 
		
		
		
		
	Жизнь прекрасна! Если, конечно, правильно подобрать антидепрессанты...  
			 | 
| 
	
 | 
| 
			
			 | 
		#4 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Стандартный локализованный функционал, заметьте  
		
		
		
		
		
		
		
		
			   В буржуйском функционале я че-то не видел, чтобы кусок бизнес-логики принимал решение о необходимости заполнения тех или иных полей на основании того, отмаплены ли они на поля определенного Map'а. Это то, что mazzy называет "программистским подходом".Ну вы же сами в первом сообщении и описали, как. Последний раз редактировалось gl00mie; 02.06.2011 в 11:00.  | 
| 
	
 | 
| 
			
			 | 
		#5 | 
| 
			
			 NavAx 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Мне? 
		
		
		
		
		
		
			Цитата: 
	
Я больше склоняюсь к тому, чтобы сам вендор и поправил... Я описывал, как он создается, но при исполнении в пакете нет клиента, на котором он мог бы создаться  , так что ему приходится довольствоваться WorkerThread'ом.Что же касается этого WorkerThread, то во-первых, он выполняется внутри процесса AOS, а во-вторых, судя по моим изысканиям и наблюдаемому поведению, является чем-то средним, возможно, некоей модифицированной для выполнения в контексте AOS клиентской частью, особенности реализации которой и приводят к подобным эффектам. Но это - только моё мнение, пока же будем исходить из того, что все это - исполняется в терминах Аксапты "на сервере". P.S. Уважаемый, вы в ФИДО, случайно, не участвовали? ![]() Фигурный квотинг, подмена предмета разговора по ходу треда, ну и потребность в последнем слове...  
		
				__________________ 
		
		
		
		
	Жизнь прекрасна! Если, конечно, правильно подобрать антидепрессанты...  
			 | 
| 
	
 | 
| 
			
			 | 
		#6 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Предлагаю, как и прежде, относиться к ней с настороженностью и должным скепсисом, а не расслабляться только оттого, что это, мол, "стандартный функционал".Представители вендора на этот счет говорят буквально следующее: 
		
		
		
		
		
		
		
	Цитата: 
	
Цитата: 
	
   В каких-то случаях обращения к нему прокатывают, а в каких-то - вылезает ошибка, мол, объект не инициализирован (это, в частности, было словлено при попытке вызова infolog.viewUpdate() из COM-коннектора).А как же!..Цитата: 
	
 
		 | 
| 
	
 | 
| 
			
			 | 
		#7 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 Цитата: 
	
Как раз только что разбирался с парой проблем при разноске накладных в пакетном режиме (для нас актуально - у нас розница, продажи магазинов разносятся в пакете ночью, в среднем 60 накладных по 1 тыс.строк). И тоже наткнулся на этот mappingExists_RU. Это, я вам скажу, нечто. Обе проблемы именно в нем. Первое:Ax32.exe именно благодаря ему кушает оперативную память (и не отдает потом обратно) со скоростью ~9 Мб на 1 тыс.строк накладной. То есть за одну ночь кушает не менее 0,5 Гига памяти. Второе: именно исполнение этого метода, как выяснилось, занимает где-то 75% времени всей разноски (а все остальное - генерация и разноска накладной, со всеми инвентрансами, инвойсжурами и прочими налогами - только 25%, а то и меньше). И вот что с этим делать? Больше даже беспокоит память, чем производительность. Нет ли у кого опыта борьбы с этой гадиной mappingExists_RU? Последний раз редактировалось Zabr; 09.06.2011 в 01:28.  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: Daiver (1). | |
| 
			
			 | 
		#8 | 
| 
			
			 NavAx 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Я подумывал о кэшировании результатов вызова в глобальном кэше. 
		
		
		
		
		
		
			Ну или перепилить его по совету gloomie на использование UtilElements. А еще есть неудачные сборки Ax32.exe\Ax32Serv.exe, ужасно "текущие". Я боролся подбором удачной комбинации версии клиента/AOS.  
		
				__________________ 
		
		
		
		
		
			Жизнь прекрасна! Если, конечно, правильно подобрать антидепрессанты...  
			Последний раз редактировалось Maximin; 09.06.2011 в 09:25.  | 
| 
	
 | 
| 
			
			 | 
		#9 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			К сожалению, в UtilElements можно узнать лишь то, "отмаплена" ли вообще таблица на тот или иной Map, а то, какие поля у нее с чем в Map'е сопоставлены, узнать можно лишь через TreeNode. Так что тут самое простое решение - именно кэшировать информацию, возвращаемую этим классом.
		 
		
		
		
		
		
		
		
	 | 
| 
	
 | 
| 
			
			 | 
		#10 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			На самом деле, UtilElements можно использовать и для проверки сопоставления полей 
		
		
		
			При выполнении на клиенте, у меня получалось сопоставимое время с MappingExists_RU (процентов на 10 быстрее, правда, память никуда не течет, вот досада  ). А вот при выполнении на сервере - разница уже в 5-6 раз не в пользу MappingExists_RU (и раза в два быстрее, чем на клиенте).Ну и кэширование - тут без вопросов Пара методов в класс Global - по использованию и результатам полностью аналогичны MappingExists_RU. Отличие между методами: mappingExists_UE использует кэширование (класс MappingsInfoCache), а mappingExists_UENC - напрямую создает классы MappingsInfo_UE X++: static public boolean mappingExists_UE(tableId _mapId, tableId _tableId, fieldId _mapField = 0, fieldId _tableField = 0) { boolean ok = false; MappingsInfoCache mappingsInfoCache = MappingsInfoCache::Construct(); ; if (mappingsInfoCache && mappingsInfoCache.MappingTableExists(_mapId, _tableId)) { if (_mapField || _tableField) { ok = mappingsInfoCache.MappingFieldExists(_mapId, _tableId, _mapField, _tableField); } else ok = true; } return ok; } static public boolean mappingExists_UENC(tableId _mapId, tableId _tableId, fieldId _mapField = 0, fieldId _tableField = 0) { boolean ok = false; MappingsInfo_UE mappingsInfo = new MappingsInfo_UE(_mapId, _tableId); ; if (mappingsInfo && mappingsInfo.MappingTableExists()) { if (_mapField || _tableField) { ok = mappingsInfo.MappingFieldExists(_mapField, _tableField); } else ok = true; } return ok; } Думаю, в четверке тоже должно запускаться. 
				__________________ 
		
		
		
		
	Axapta v.3.0 sp5 kr2  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: Zabr (2), raz (5), Daiver (1). | |
| 
			
			 | 
		#11 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Мы в итоге (спасибо wolfstein) пошли по еще более простому пути  - заменили вызов mappingExists_RU на сравнение tableid с перечнем конкретных таблиц. Утечка памяти ликвидирована полностью, скорость разноски тоже немного увеличилась (хотя и не так сильно, как я ожидал).
		 
		
		
		
		
		
		
		
	 | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: someOne (3). | |
| 
			
			 | 
		#12 | 
| 
			
			 Боец 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Мы сделали так. Скорость разноски увеличилась на порядок. 
		
		
		
		
		
		
		
		
			1. Обявляем в классе \Classes\Application.classDeclaration переменную X++: Map                     mappingsInfo_RU; // Оптимизация разноски накладной, EVO 26.04.2011X++: // Оптимизация разноски накладной, EVO 26.04.2011 Map mappingsInfo_RU() { ; if (!mappingsInfo_RU) { mappingsInfo_RU = new Map(Types::Container, Types::Class); // [TableId, MapId], MappingsInfo_RU object } return mappingsInfo_RU; } X++: static public Map createMapWithFieldId(tableId _tableId, tableId _mapID) { MappingsInfo_RU mappingsInfo; ; // Оптимизация разноски накладной, EVO 26.04.2011 --> //orig mappingsInfo = new MappingsInfo_RU(_tableId, _mapID); //orig return mappingsInfo.find(); if (!appl.mappingsInfo_RU().exists([_tableId, _mapID])) { mappingsInfo = new MappingsInfo_RU(_tableId, _mapID); appl.mappingsInfo_RU().insert([_tableId, _mapID], mappingsInfo.find()); } return appl.mappingsInfo_RU().lookup([_tableId, _mapID]); // Оптимизация разноски накладной, EVO 26.04.2011 <-- } Последний раз редактировалось DSPIC; 09.06.2011 в 23:23.  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: UNRW (1), wolfstein (1). | |
| 
			
			 | 
		#13 | 
| 
			
			 Administrator 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Идиотский вопрос: А зачем новый метод в классе Application? Почему не использовать appl.globalcache() ? Там такой же Map.
		 
		
		
		
		
		
		
			
				__________________ 
		
		
		
		
	Возможно сделать все. Вопрос времени  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: DSPIC (5). | |
| 
			
			 | 
		#14 | 
| 
			
			 Боец 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Так нагляднее  
		
		
		
		
		
		
		
	![]() Если серьезно, можно и через кэш. Я сильно не заморачивался, сделал за ~10 минут по-проще.  | 
| 
	
 | 
| 
			
			 | 
		#15 | 
| 
			
			 Administrator 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Не... я ж ничего не имею против  
		
		
		
		
		
		
			 . Просто обычно - затрагивание такого плана класса как Application приводит к сложностям к портированию кода (аос какой-нибудь рестартануть придется; может даже вынужденно, локальный кеш у юзеров может слететь и т.д.).Поэтому и спросил. 
				__________________ 
		
		
		
		
	Возможно сделать все. Вопрос времени  | 
| 
	
 | 
| 
			
			 | 
		#16 | 
| 
			
			 Axapta Retail User 
		
			
	 | 
	
	
	
		
		
		
		 
			
			С использованием appl.globalcache() - модифицируем метод \Classes\MappingsInfo_RU\createMapWithFieldId следующим образом: 
		
		
		
		
		
		
		
	X++: static public Map createMapWithFieldId(tableId _tableId, tableId _mapID) { MappingsInfo_RU mappingsInfo; ; //mappingsInfo = new MappingsInfo_RU(_tableId, _mapID); //return mappingsInfo.find(); if (!appl.globalCache().isSet(classstr(MappingsInfo_RU), [_tableId, _mapID])) { mappingsInfo = new MappingsInfo_RU(_tableId, _mapID); appl.globalCache().set(classstr(MappingsInfo_RU), [_tableId, _mapID], mappingsInfo.find()); } return appl.globalCache().get(classstr(MappingsInfo_RU), [_tableId, _mapID]); }  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: DSPIC (5). | |
| 
			
			 | 
		#17 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			MappingsInfo_RU - может жить и на клиенте. 
		
		
		
		
		
		
			Так что сохранять его экземпляр исключительно в кэше сервера - неправильно 
				__________________ 
		
		
		
		
	Axapta v.3.0 sp5 kr2  | 
| 
	
 | 
| 
			
			 | 
		#18 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 Цитата: 
	
Собрал статистику вариантов входных параметров, которые передаются ей на вход. Для нашего приложения (Ax2009 RU6) их оказалось всего лишь 13 комбинаций. Но самое интересное в том что из всех этих вариантов она никогда не возвращает "false". Результат всегда один и тот же: "true" ! Возникает вопрос - зачем эта функция вообще нужна ? к примеру в методе класса SalesPurchReport_Invoice4Paym_RU она используется так X++: if (mappingExists_RU(tablenum(CustVendInvoiceJour), invoiceJour.TableId, fieldnum(CustVendInvoiceJour, rContractCode))) так как invoiceJour в этом методе всегда VALUE: map = CustInvoice4PaymJour_RU и никак иначе. Или я не прав ? Короче, заменил эту функцию на X++: return true;  | 
| 
	
 | 
| 
			
			 | 
		#19 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Кажется для случая PackingSlip по закупке будет false 
		
		
		
		
		
		
		
	Возможно у вас это не используется. Мне кажется самый безопасный способ - сделать кеширование результатов. Изменений минимум. Поддерживать легко. Если в других местах появится активное использование этой функции - то все равно быстро будет работать.  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: someOne (3). | |
| 
			
			 | 
		#20 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 Цитата: 
	
		
			Сообщение от Logger
			 
 
			Кажется для случая PackingSlip по закупке будет false 
		
	Возможно у вас это не используется. Мне кажется самый безопасный способ - сделать кеширование результатов. Изменений минимум. Поддерживать легко. Если в других местах появится активное использование этой функции - то все равно быстро будет работать. Сделал в итоге как написал wolfstein Некорректное отражение map при пакетной обработке в Ax2009, и налоги в строках накладных/фактур (Спасибо ему). Кажется, печатные формы стали работать быстрее. Всем рекомендую  
		Последний раз редактировалось someOne; 21.12.2011 в 15:08.  | 
| 
	
 | 
| Теги | 
| bug, map, treenode, баг, локализация, накладная, налоги, ошибка, счет-фактура | 
| 
	
	 | 
	
		
		
  |