Добрый день.
Стоит задача сделать аналог фиксированного фильтра "Ответственный" в р. Контрагенты.
Фильтрация происходит по детали "Сервисы контрагента".
Фильтр в AccountSectionV2:

 initFixedFiltersConfig: function() {
                    var fixedFilterConfig = {
                        entitySchema: this.entitySchema,
                                                        filters: [
                             {
                                                                        name: "AccountServiceItem",
                                                                        caption: resources.localizableStrings.AccountServiceItemFilterCaption, //"Cервисы контрагента"
                                                                        dataValueType: this.Terrasoft.DataValueType.LOOKUP,
                                    //filter: this.getAccountServiceFixedFilter,
                                                                        appendFilter: this.getAccountServiceFixedFilter,
                                                                        columnName:"UsrServiceItem",
                                    //defValue: null,
                                    referenceSchemaName:"UsrAccountServiceItem",
                                    markerValue: "AccountServiceItemFixedFilterBtn",
                                    buttonImageConfig: resources.localizableImages.AccountServiceItemFilterImage,//путь к картинке Сервисов
                                    hint: "",
                                                                    appendCurrentContactMenuItem : false,
                                    addNewFilterCaption : resources.localizableStrings.AccountServiceItemAddNewFilterCaption, //"Выбрать сервисы",
                                    isCustomFilter: true //нужен для запуска пользовательской логики построения фильтра
                                }
                            ]
                    };
                    this.set("FixedFilterConfig", fixedFilterConfig);
                },

                getAccountServiceFixedFilter: function(filterInfo){
                    //Фильтр по детали AccountServiceItem
                    debugger;
                    var filter;
                    if (!Ext.isEmpty(filterInfo.value) && filterInfo.value.length > 0) {
                        filter = Terrasoft.createColumnInFilterWithParameters(
                                                        "[UsrAccountServiceItem:UsrAccount:Id].UsrServiceItem.Id", filterInfo.value);
               
                    }
                    return filter;

                }

Создал замещающий клиентский модуль FixedFilterViewModelV2 (см. прикреплённый файл).

Фильтр работает, НО после перехода между разделами фильтр перестаёт работать, хотя getAccountServiceFixedFilter отрабатывает и контейнер фиксированного фильтра отображается.

Подскажите, кто сталкивался с подобной задачей, что я упустил из виду.
Версия 7.8.0.1005_CustomerCenter

Нравится

18 комментариев

Коллега, вот же есть идеальная статья по созданию быстрого фильтра - https://academy.terrasoft.ru/documents/technic-sdk/7-7-0/kak-dobavit-v-…

Иначе нужно дебажиться

Илья, в указанной статье показан пример добавления стандартных фильтров.
Мне же нужен фильтр по аналогии с фильтром "Ответственный".
Он реализован, но не срабатывает после перехода между разделами, как я писал выше

Здравствуйте!

Может проблема в асинхронности вызова - фиксированные фильтры раздела приходят после загрузки реестра?

Однако, Фильтр "Ответственный" в разделе Активности отрабатывает корректно. А я делал по его аналогии.

Предоставьте, пожалуйста, листинг AccountSectionV2.

"Демьяник Алексей" написал:Предоставьте, пожалуйста, листинг AccountSectionV2.

В NUI.ActivitySectionV2 переопределен метод init():

				init: function(callback, scope) {
					this.set("SelectedRows", []);
					this.initFiltersUpdateDelay();
					this.registerGetIsVisibleEmailPageButtonsHandler();
					this.initScheduleGridData();
					this.initSchedulerFloatItemsCollection();
					this.initSchedulerTimeScaleLookupValue();
					this.initMailBoxSyncSettings();
/* вот здесь вызывается метод, который фильтрует записи*/
					this.initFixedFiltersConfig();
					this.initIntervalMenuItems();
					this.callParent([function() {
						this.initSchedulerItemsAmountPerPage(function() {
							callback.call(scope);
						}.bind(this));
					}, this]);
					var sysSettings = ["BuildType"];
					Terrasoft.SysSettings.querySysSettings(sysSettings, function() {
						var buildType = Terrasoft.SysSettings.cachedSettings.BuildType &&
							Terrasoft.SysSettings.cachedSettings.BuildType.value;
						this.set("canUseSyncFeaturesByBuildType", buildType !==
						ConfigurationConstants.BuildType.Public);
					}, this);
					this.initGoogleSyncExists();
					this.initGoogleCalendarLog();
				},

Предполагаю, что Ваш метод не вызывается на init раздела (судя по листингу кода). Отсюда - раздел не фильтруется.

Вызывается, т.к. я переопределил и FixedFilterViewModelV2 (файл прикреплён в первом посте) и добавил свой функционал в метод init (см. блок строк 416-432).

Здравствуйте,
советую не трогать фиксированные фильтры, и воспользоваться решением, предоставленным тут:
http://www.community.terrasoft.ua/forum/topic/15943#comment-60027
По факту это будет то же самое, лукап в панеле фильтров, завязанный на атрибут (вирутальный) по изменению которого вы будете накладывать любую доп. фильтрацию на раздел.
Проверил все на 7.8. работает, и багов при переходе между разделами так же не заметил. Вот пример для раздела контрагентов, фильтрую по значению лукапа с типом город, по детали адреса контрагентов:

Сама замещающая страница секции контрагентов:

define("AccountSectionV2", ["BaseFiltersGenerateModule", "css!UsrMyFilterStyle"], function(BaseFiltersGenerateModule) {
	return {
		entitySchemaName: "Account",
		details: /**SCHEMA_DETAILS*/{
		}/**SCHEMA_DETAILS*/,
		diff: /**SCHEMA_DIFF*/[
			{
				"operation": "insert",
				"name": "MyFilterContainer",
				"parentName": "LeftGridUtilsContainer",
				"propertyName": "items",
				"index": 0,
				"values": {
					"id": "MyFilterContainer",
					"itemType": this.Terrasoft.ViewItemType.CONTAINER,
					"items": []
				}
			},
			{
				"operation": "insert",
				"parentName": "MyFilterContainer",
				"propertyName": "items",
				"name": "UsrCityFilter",
				"values": {
					"bindTo": "UsrCityFilter",
					"caption": "City"
				}
			}
		]/**SCHEMA_DIFF*/,
		attributes: {
			"UsrCityFilter": {
				"dataValueType": Terrasoft.DataValueType.LOOKUP,
				"type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
				isLookup: true,
				caption: "City",
				referenceSchemaName: "City"
			}
		},
		mixins: {
			LookupQuickAddMixin: "Terrasoft.LookupQuickAddMixin"
		},
		methods: {
 
			onLookupChange: function(newValue, columnName) {
				this.callParent(arguments);
				if (columnName === "UsrCityFilter") {
					this.set("UsrCityFilter", newValue);
					this.onUsrCityFilterChanged();
				}
			},
 
			onUsrCityFilterChanged: function() {
				this.reloadGridData();
			},
 
			initQueryFilters: function(esq) {
				this.callParent(arguments);
 
				var usrCityFilter = this.get("UsrCityFilter");
 
				if (usrCityFilter && usrCityFilter.value) {
					esq.filters.add("UsrCityFilter", this.Terrasoft.createColumnFilterWithParameter(
						this.Terrasoft.ComparisonType.EQUAL, "[AccountAddress:Account:Id].City", usrCityFilter.value));
				} else {
					esq.filters.removeByKey("UsrCityFilter");
				}
			}
 
		}
	};
});

Дополнительные стили для расположения фильтра в том же ряду что и базовые (UsrMyFilterStyle):

#MyFilterContainer {
	display: inline-block;
	float: left;
	position: relative;
	padding-top: 5px;
}
 
#AccountSectionV2UsrCityFilterContainer_Label {
	max-width: 3em;
	min-width: 3em;
}
 
#AccountSectionV2UsrCityFilterContainer_Control {
	width: auto;
}

Результат:

Указанный фильтр не предусматривает множественного выбора, что требуется по моей задаче

Здравствуйте, развивая идею с фильтрацией в initQueryFilters: доработайте поле выбора пользовательским методом открытия формы подбора, примеров справочников с мультивыбором в системе достаточно, можно поискать по ключевому слову «multiSelect: true».
К примеру, в «EmailPageV2» такие есть:

Для контрола «текст» там задается иконка выбора и обработчик её нажатия:

"controlConfig": {
   "className": "Terrasoft.TextEdit",
   "rightIconClasses": ["custom-right-item", "lookup-edit-right-icon"],
   "rightIconClick": {
      "bindTo": "openRecepientLookupEmail"
   }
}

Потом в методе открывается своя форма подбора с мультивыбором, мультивыбор задается в конфиге:

getLookupConfig: function(columnName) {
   var scope = this;
   var callback = function(args) {
      scope.onLookupSelected(args);
   };
   return {
      config: {
         entitySchemaName: "VwRecepientEmail",
         columnName: columnName,
         columns: ["ContactId"],
         filters: Terrasoft.createColumnIsNotNullFilter("ContactId"),
         multiSelect: true
      },
      callback: callback
   };
},

Результат обрабатывается колбеком onLookupSelected
В теле которого само «текстовое» представление фильтра заполняется перечислением выбранных значений с произвольным разделителем, в примере EmailPageV2 это точка с запятой.

Далее, параллельно, в обработчике выбора запишите список значений в атрибут типа «массив», и в методе initQueryFilters стройте группу фильтров, в которые через логическое ИЛИ помещайте выбранные значения. Все, мультивыбор есть, фильтрация с ИЛИ по выбранным значениям есть. Да задача достаточно массивная и требует навыков разработчика, готовый большой пример быстро не написать. Но алгоритм приблизительно таков как я описал в дополнение к предыдущему комментарию. Если где-то возникнут точечные трудности и вопросы, задавайте, постараюсь помочь.

Илья, спасибо. Сам дошёл до этого варианта. Но при этом плывёт вёрстка и контейнер с фильтрами накладывается на фиксированные фильтры. + при переходе м/у разделами фильтр обнуляется.

На данный момент с фильтром есть две проблемы:
1.После перехода между разделами фильтр очищается. Как сохранить наполнение фильтра?
2. При открытии справочного поля необходима сортировка наполнения справочника. При открытии справочника я передаю в config массив:

sortedColumns:  [{
  name:"Name",
  orderPosition: 0,
  orderDirection: Terrasoft.OrderDirection.ASC
}]

В LookupPageViewModule.initSelectSorting сортировка применяется, однако в GridProfileHelper.initSelectSorting применяется стандартная сортировка по полю для отображения. По умолчанию для справочника это и есть поле Name, т.о. два правила сортировки применяются к одному и тому же полю. В итоге справочник не отсортирован. Как отключить стандартную сортировку?

1. Создать таблицу для кеша значений фильтрации, с колонкой «Ид пользователя», и текстовой строкой, в которую будете при onLookupChange новой фильтрации писать JSON массив значений поля сортировки (в том числе при очистке фильтра, в кеш писать тоже пустой JSON массив), а в «initQueryFilters» проверять таблицу кеша через esq запрос. Если в атрибуте пусто, а в кеше отличный от пустого JSON, восстанавливаете сохраненную в кеше фильтрацию.

2. Приоритет отдается сортировке, сохраненной в профиле, следовательно, либо очистить профиль, и тогда предсохраненной сортировки не останется, будет работать ваша, либо переписывать метод «initSelectSorting» в «GridProfileHelper», проверяя наличие в колонках из select атрибутов “orderDirection”, и если таковые есть, вовсе не применять к этим колонкам сортировку из «profileSortedColumns», но этим вы напрочь запретите пользователю изменить порядок сортировки через кнопку «Вид» -> «Сортировка», в форме выбора лукапа, для полей, сортировка которых передана программно. В общем, если решите замещать «GridProfileHelper», то часть коробочного функционала вы потеряете.

"Мотков Илья" написал:1. Создать таблицу для кеша значений фильтрации, с колонкой «Ид пользователя», и текстовой строкой, в которую будете при onLookupChange новой фильтрации писать JSON массив значений поля сортировки (в том числе при очистке фильтра, в кеш писать тоже пустой JSON массив), а в «initQueryFilters» проверять таблицу кеша через esq запрос. Если в атрибуте пусто, а в кеше отличный от пустого JSON, восстанавливаете сохраненную в кеше фильтрацию.

А разве стандартые фильтры (как быстрые, так и фиксированные) используют таблицу для кэша? Можно подробней о вашем варианте?

QuickFilterModule используют StorageUtilities, что по сути, что-то вроде document.scope в своем пространстве и с методами для хранения по группам, или по ключам. Можете использовать такой кеш, это решит вашу проблему с переходом по разделам. Но при повторном логине, или в другом браузере ваши фильтры слетят в ноль, и при установке будут жить в рамках сеанса.

FolderFilters, FixedFilters, TagFilters так же сохраняются в профиль, то есть, в таблицу в базе данных SysProfileData, к примеру:
SELECT * FROM SysProfileData WHERE [Key] = 'ActivitySectionV2GridDataViewFilters'

Это решает сохранение фильтров даже при выходе и заходе в приложение. В зависимости от того что вам необходимо, используйте первый или второй подход. Храните информацию в document.scope или в StorageUtilities. Либо создавайте свою таблицу для кеша и пишите в неё. Либо можете писать в SysProfileData, только используйте для этого свой уникальный ключ.

P.S. почитайте saveFilter в BaseSectionV2

Спасибо Илья!

Показать все комментарии

Потрібно створити фільтр, який би гарантував пусту вибірку.
Я собі так уявляю, що фільтр має реалізовувати умову типу 0=1.
Але всі методи на створення фільтрів так чи інакше одним з атрибутів мають поле.

Як правильно реалізувати фільтр ?

Нравится

3 комментария

Может, такое сработает?

esq.CreateIsNullFilter("Id");

А [Id] ні за яких умов не стане Guid.Empty() ?

Оно же первичный ключ. Кроме того, фильтр проверяет не Guid.Empty (из нулей), а Null.

Показать все комментарии
Добавить на форум возможность фильтровать темы, например "с новыми ответами" или "без ответов".
2 комментария

http://www.community.terrasoft.ru/user/51853/track
(кнопка следить в Вашем профайле)
:wink:

Да, но это немного не то. Эта страница показывает только те страницы, на которых есть мои посты, а я в большей степени имел ввиду фильтрацию, допустим в определенном форуме вопросов без ответов, т.е. НОВЫЕ темы или вопросы или блоги интересных мне людей!

Показать все комментарии

Для того, чтобы настроить фильтрацию одного поля справочника по другому, необходимо добавить в обработчик события Init карточки редактирования программный код фильтрации. То есть, подписаться на обработчик события изменения значения поля.

Пример:

Откройте раздел Инструменты – Конфигурация, откройте Страницу редактирования конфигурационной единицы (ConfigurationItemModuleEditPage)

delegate1

Посмотрите в карточке названия полей, которые необходимо будет связать между собой. Например, нам надо фильтровать список контактов по выбранному в соответствующем поле контрагенту:

delegate2

Откройте процесс карточки:

delegate3

В процессе найдите скрипт обработчика события Init:

delegate4

В скрипт обработчика добавьте подписывание делегата:

Page.ContactEdit.PrepareLookupFilter += delegate (object sender, LookupEditEventArgs e) // либо
 {     
        if (!Page.AccountEdit.Value.Equals(Guid.Empty)) {
                CollectionDictionarystring, object>> filters = e.Filters;
                filters.Add(new Dictionarystring, object> {
                                   {"comparisonType", FilterComparisonType.Equal},
                                   {"leftExpressionColumnPath", "Account.Id"},
                                   {"useDisplayValue", false},
                                   {"rightExpressionParameterValues", new object[] {Page.AccountEdit.Value}}});
        }
};

Где
AccountEdit – название поля в карточке, по которому фильтруем
ContactEdit – название поля в карточке, для которого производится фильтрация
Account - название поля в объекте, которое будет сопоставляться со значением AccountEdit
LookupEditEventArgs - параметр, указывающий на тип поля. LookupEditEventArgs - если это поле справочника, ComboBoxEditEventArgs - если это поле с выпадающим списком.

Затем создайте обработчик события AccountEditChange:

delegate5

В обработчик события добавьте следующий программный код:

Page.ContactEdit.Clear(); //если у нас поле справочника
Page. ContactEdit.ListPrepared = false; //если у нас поле с выпадающим списком

Обратите внимание, если обработчик уже создан, нет необходимости создавать его повторно, код следует добавить в существующий.

После добавления программного кода опубликуйте карточку для применения изменений:

delegate6

delegate7

Примечание: если Вам нужно фильтровать результат по нескольким полям, тело делегата будет иметь следующий вид:

var filters = e.Filters;
object value = Page.CountryEdit.Value;
Guid countryId = (value == null || value.ToString().Equals(string.Empty)) ? Guid.Empty : Guid.Parse(value.ToString());
value = Page.RegionEdit.Value;
Guid regionId = (value == null || value.ToString().Equals(string.Empty)) ? Guid.Empty : Guid.Parse(value.ToString());
if (countryId != Guid.Empty) {
        filters.Add(new Dictionarystring, object> {
                {"comparisonType", FilterComparisonType.Equal},
                {"leftExpressionColumnPath", "Country.Id"},
                {"useDisplayValue", false},
                {"rightExpressionParameterValues", new object[] {countryId}}});
}
if (regionId != Guid.Empty) {
        filters.Add(new Dictionarystring, object> {
                {"comparisonType", FilterComparisonType.Equal},
                {"leftExpressionColumnPath", "Region.Id"},
                {"useDisplayValue", false},
                {"rightExpressionParameterValues", new object[] {regionId}}});
}

Нравится

Поделиться

0 комментариев
Показать все комментарии

Добрый вечер, путник зашедший на мой блог.
Если ты еще не спишь (а завтра рано вставать) вот тебе мой совет по исправлению Delete Query на администрируемую по записям таблицу, который использует Exist фильтры со ссылками на основную таблицу Delete Query.
Если ты, дорогой путник, не заснул после предыдущей фразы, вот тебе функция, которая исправляет данную проблему:

function ReplaceDQTablesAliasesByViewsInExistFilters(DeleteQuery, TableName, ViewName) {
        var IsAdmin = Connector.CurrentUser.IsAdmin;
        if (IsAdmin) {
                return;
        };             
        var Filters, ExistFilter, Filter, LeftTableAlias, RightTableAlias;
        for (var i = 0; i DeleteQuery.Filters.Count; i++) {
                ExistFilter =
                        DeleteQuery.Filters.Items(i);
                if (ExistFilter.FilterType != ftExists) {
                        continue;
                };
                for (var j = 0; j ExistFilter.TestExpression.ExpressionSelectQuery.Count; j++) {
                        Filters = ExistFilter.TestExpression.ExpressionSelectQuery.Items(j).Filters;
                        for (var k = 0; k Filters.Count; k++) {
                                Filter = Filters.Items(k);
                                if (!IsStringInArray(Filter.FilterType, [ftIsNull, ftCompare, ftLike, ftBetween, ftInclude])) {
                                        continue;
                                };
                                LeftTableAlias = Filter.TestExpression.TableAlias;
                                if (!IsEmptyValue(LeftTableAlias)) {
                                        Filter.TestExpression.TableAlias =
                                                LeftTableAlias.replace(TableName, ViewName);
                                };
                                if (Filter.FilterType == ftCompare) {
                                        RightTableAlias = Filter.ValueExpression.TableAlias;
                                        if (!IsEmptyValue(RightTableAlias)) {
                                                Filter.ValueExpression.TableAlias =
                                                        RightTableAlias.replace(TableName, ViewName);
                                        };                                     
                                };                             
                        }
                }
        }
}

Данная проблема может возникнуть где-угодно в версиях вплоть до 3.2.1.17 (а может и выше), но в базовой версии на написание сего меня подвергла операция "Раздать права доступа как у родительского элемента" детали Файлы. Под пользователем данная операция просто валится, т.к. для выполнения использует dq_GiveRightsByParentItem, которая не лишена проблемы описанной выше. Для исправления можно в scr_FilesDetailGridArea подправить ф-ию DeleteExistingFileAccessRecordsByParentItem: перед "DeleteQuery.Execute()" вызвать ф-ию описанную выше как-то так:

ReplaceDQTablesAliasesByViewsInExistFilters(DeleteQuery, 'tbl_FilesRight', 'vw_FilesRight');

Я не сомневаюсь, дорогой путник, что ты давно спишь. Счастливых тебе снов.

Нравится

Поделиться

0 комментариев
Показать все комментарии