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

Есть задача скрыть редактирование поля страны в детали по организационным ролям(супервайзер, руководители, менеджеры), закрыть право на редактирование для менеджеров.
Пробывал разные варианты через конфигурацию и добавления правил, все равно не работает.
Версия bpm7.6.

Как лучше мне это сделать?

Нравится

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

Здравствуйте, Сергей!

Вы можете видимость поля задать бизнес правилом. Параметр видимости будет определяться в момент открытия страницы редактирования (Вам необходимо переопределить метод onEntityItialized()). Замещенный метод onEntityItialized() должен вызывать родительский, а также проверять вхождение пользователя в роли (используйте ESQ по объекту SysAdminUnitInRole). Обратите внимание, что один пользователь может входить в несколько ролей.

Сформировал запрос к таблице SysAdminUnitInRole по своей Id

SELECT *
FROM            dbo.SysAdminUnit sa LEFT JOIN
                         dbo.SysAdminUnitInRole saur ON sa.Id = saur.SysAdminUnitId
WHERE sa.Id = 'мой Id'

Вернуло 3 строки - это означает, что в меня есть 3 роли?

Да, это значит что Администратор с sa.Id = 'мой Id' входит в три роли
Если вы выполните

SELECT saur.SysAdminUnitId, saur.SysAdminUnitRoleId
FROM            dbo.SysAdminUnitInRole saur
Where saur.SysAdminUnitId = 'мой Id'

То увидите айдишники ролей

Спасибо за подсказку, пытался вывести название ролей:

SELECT saur.SysAdminUnitId, saur.SysAdminUnitRoleId, s.Name
FROM            dbo.SysAdminUnitInRole saur Left JOIN
                         dbo.SysAdminUnit s ON saur.SysAdminUnitId = s.Id
WHERE saur.SysAdminUnitId = 'мой Id'

Результат имя моего контакта. Может я неправильно делаю?

так ваш запрос и выводит имя контакта из s.Name (SysAdminUnit)

можно сделать так

select s.Name 
from SysAdminUnit s
where s.id in (
select saur.SysAdminUnitRoleId
from dbo.SysAdminUnitInRole saur
where saur.SysAdminUnitId = 'мой Id'
)

Вот только одна запись похоже тоже будет вашим контактом, т.к. есть соответствие одинаковых id в SysAdminUnitInRole

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

Добрый день!

Существует ли возможность указания в url'е группы для перехода, а так же доп.фильтра?

Нравится

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

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

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

"Демьяник Алексей" написал:Вы можете создать собственные команды для командной строки для перехода в динамические группы. В результате выполнения команды будет открываться та или иная группа с предустановленными фильтрами.

Спасибо, Алексей! А команду можно указать в url'е?

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

Эмулировать команду в url нет возможности.

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

Возникает проблема в
(anonymous function) (SchemaBuilderV2.js:338)
(anonymous function) (SchemaBuilderV2.js:126)
(anonymous function) (commonutils.js:1463)

в строке:

generatorConfig.entitySchemaName = schemaResponse.entitySchemaName; //schemaResponse = undefined

Что нужно проверить, чтоб schemaResponse был "живым"?

Нравится

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

Опишите подробнее на каком этапе появляется ошибка?

Очистил кэш браузера, всё заработало.

Видимо после добавления нового поля нужно было кэш чистить...

"AlexLS" написал:Что нужно проверить, чтоб schemaResponse был "живым"?

Добрый день!!!

проверять ничего не требуется, нужно знать четкие правила, что при создание новых разделов, страниц редактирования, деталей их обязательно нужно регистрировать в базе данных в таблицах SysModule, SysModuleLcz, SysModuleEdit, SysModuleEditLcz, SysDetail. И тогда подобные ошибки возникать у вас или вообще не будут или будут возникать очень редко.

"Власов Михаил Викторович" написал:их обязательно нужно регистрировать в базе данных в таблицах SysModule, SysModuleLcz, SysModuleEdit, SysModuleEditLcz, SysDetail

Михаил, спасибо за ответ! А как это правильно делать, где-то сказано?

"AlexLS" написал:Михаил, спасибо за ответ! А как это правильно делать, где-то сказано?

По данному вопросу есть информация на Академии, плюс данные вопросы уже не раз обсуждали на комьюнити, если поискать то можно найти ответа. Я своей практики написал сам SQL скрипты по регистрации Разделов, Страниц редактирования, Деталей и пользуюсь ими. Мне так удобнее и быстрее. Если нужно могу поделиться.

"Власов Михаил Викторович" написал:SQL скрипты по регистрации Разделов, Страниц редактирования, Деталей и пользуюсь ими. Мне так удобнее и быстрее. Если нужно могу поделиться

Михаил, спасибо!

Буду очень благодарен, если поделитесь (например, в личном сообщении)!

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

Добрый день!

Кто-то баловался с переключением clientUnits useFileContent="true" / "false" ?

Появилась потребность новых разделов, решил "наклепать" мастером, так как вроде приятнее и проще там делать. Но вот незадача - обратное переключение не показывает "мастеровые" метаданные. Удалял все js-файлы, и делал "обновить клиентские модули из базы данных", но ощущение что есть две разных ветки клиентских модулей, так как переключение между режимами даёт разные картинки. Что же не так делаю?

Нравится

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

Добрый день.
Для первичного создания нового раздела проблем не будет даже при useFileContent="true". После создания раздела нужно обновить клиентские модули из БД.

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

Обратите внимание, что мастер может затереть subscriber для деталей, если код указан в теле функции, а не ссылка на метод.

"Пащенко Александр Сергеевич" написал:Для первичного создания нового раздела проблем не будет даже при useFileContent="true". После создания раздела нужно обновить клиентские модули из БД.

Как-то появляются проблемы: кусками данные есть.

"Пащенко Александр Сергеевич" написал:После повторного включения необходимо выполнить действие "Обновить клиентские модули из БД".

Делаю, но проблема не лечится.

"Пащенко Александр Сергеевич" написал:Обратите внимание, что мастер может затереть subscriber для деталей, если код указан в теле функции, а не ссылка на метод.

Не очень понятно. Задача тупо создать новый раздел с новыми деталями (ну и подкорректировать, при необходимости) мастером...

Здравствуйте.
Попробуйте выполнить следующее: включить usefilecontent и обновить клиентские модули из бд. После этого выполнить очистку кеша.

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

В описании (которого почему-то не нашел в оглавлении) http://academy.terrasoft.ru/documents/docs/technic/SDK/7.6.0/MobileAppli...
для секции ModelDataImportConfig есть пример с макросом. А возможно ли сделать зависимость от какого-то поля контакта пользователя?

Например у Активности есть поле Участок (справочник) и у Контакта есть такое поле Участок. Нужно чтобы Активность загружалась если её участок совпадает с участком указанным в Активности

Нравится

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

Здравствуйте.
Вы пишите "Нужно чтобы Активность загружалась если её участок совпадает с участком указанным в Активности" - это как понимать?
Опишите пожалуйста детальней - что ожидается увидеть на выходе. какой бизнес-кейс решается?

Есть Пользователь П1 (торговый представитель), с ним связан Контакт К1, у которого есть кастомное поле Участок (типа справочник) со значением У1. Есть задачки (простите Активности!) З1, З2, ...., З99999, у которых кроме всего прочего так же есть кастомное поле Участок из того же справочника. Так вот если нет надобности грузить 99999 задачек, а только те у которых поля Участок совпадают с одноименным полем Контакта, то как построить данный фильтр типа Пользователь.Контакт.Участок = Задачка.Участок.

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

В етом случае Вас тоит вносить код в секцию ModelDataImportConfig Манифеста мобильного приложения.

Пример фильтрации данных для загрузки описан в документе, приведенном Вами выше:
Активность. Загружаются все колонки. Выполняется фильтрация модели - загружаются только те активности, у которых участником является текущий пользователь.

ModelDataImportConfig: [
// Конфигурирование модели Activity.
{
Name: 'Activity',
SyncFilter: {
type: 'Terrasoft.FilterTypes.Group',
logicalOperation: 'Terrasoft.FilterLogicalOperations.Or',
subfilters: [
{
property: 'Participant',
modelName: 'ActivityParticipant',
assocProperty: 'Activity',
operation: 'Terrasoft.FilterOperations.Any',
valueIsMacros: true,
value: 'Terrasoft.ValueMacros.CurrentUserContact'
}
]
},
SyncColumns: ['Title', 'StartDate', 'DueDate', 'Status', 'Result', 'DetailedResult', 'ActivityCategory', 'Priority', 'Owner', 'Account', 'Contact',
'Lead', 'Opportunity', 'ShowInScheduler', 'Author', 'Type'
]
},

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

так ведь нет участников :smile: есть УЧАСТОК

Предоставленый код является примером реализации.
По аналогии, Вы можете настроить свою фильтрацию.

"Клименко Николай" написал:

Предоставленый код является примером реализации.

По аналогии, Вы можете настроить свою фильтрацию.


Николай, спасибо за Ваши ответы, но к сожалению нужной информации в них нет, возможно потому, что я плохо изложил задачу.

Нужно понимание как сделать фильтры по полям и в левой и в правой части выражения сравнения, что-то типа (ActivityAccount - деталь Контрагенты в Активностях):

ModelDataImportConfig: [
        // Конфигурирование модели Activity.
        {
            Name: 'ActivityAccount',
            SyncFilter: {
                type: 'Terrasoft.FilterTypes.Group',
                logicalOperation: 'Terrasoft.FilterLogicalOperations.Or',
                subfilters: [
                    {
                        property: 'PartOf',
                        valueIsMacros: true,
                        value: 'Terrasoft.ValueMacros.CurrentUserContact.PartOf'
                    }
                ]
            },    

В Вашем случае необходимо применить вариант решения с использоваием view

1. Создать View (например VwActivityAreaByContactArea) с колонками Активности и Контакта

2. Добавить в конфигурацию скрипт (Подразумевается колонка Участок = Area) и установить
------------------------------------------------------------------------
IF EXISTS (SELECT 1 FROM sys.views WHERE name = 'VwActivityAreaByContactArea')
DROP VIEW dbo.VwActivityAreaByContactArea
go
------------------------------------------------------------------------
CREATE VIEW dbo.VwActivityAreaByContactArea AS
SELECT
a.Id,
a.CreatedOn,
a.CreatedById,
a.ModifiedOn,
a.ModifiedById,
a.ProcessListeners,
a.Id as ActivityId,
c.Id as ContactId
FROM Activity a
JOIN Contact c ON c.Area = a.Area
Where c.Area <> ''

3. В манифесте добавить фильтрацию
"ModelDataImportConfig": [
{
"Name": "Activity",
"SyncFilter": {
"type": 'Terrasoft.FilterTypes.Group',
"logicalOperation": 'Terrasoft.FilterLogicalOperations.And',
"subfilters": [
{
"property": 'Participant',
"modelName": 'ActivityParticipant',
"assocProperty": 'Activity',
"operation": 'Terrasoft.FilterOperations.Any',
"valueIsMacros": true,
"value": 'Terrasoft.ValueMacros.CurrentUserContact'
},
{
"property": "Contact",
"modelName": "VwActivityAreaByContactArea",
"assocProperty": "Activity",
"operation": 'Terrasoft.FilterOperations.Any',
"valueIsMacros": true,
"value": "Terrasoft.ValueMacros.CurrentUserContact"
}
]
}
},
{
"Name": "VwActivityAreaByContactArea",
"SyncFilter": {
"property": "Contact",
"valueIsMacros": true,
"value": "Terrasoft.ValueMacros.CurrentUserContact"
}
}
]
И указать модель View

"Models": {
"Activity": {
"Grid": "MobileActivityGridPage",
"Preview": "MobileActivityPreviewPage",
"Edit": "MobileActivityEditPage",
"RequireLookupColumnsModels": true,
"RequiredModels": [

"VwActivityAreaByContactArea"
]
}

"Клименко Николай" написал:В Вашем случае необходимо применить вариант решения с использоваием view

Николай, спасибо большое! Всё гениальное... не так то и просто :sad:

Извините за некропостинг, но может найдется кто, чтобы немного мне объяснить.
Мне надо отфильтровать Обращения, по состоянию, которые НЕ закрыты. Или хотя бы для примера SolutionOverdue, который означает "закрытие".
Добавляю в манифест фильтр:

"SyncFilter": {
					"property": "Status",
// Название модели, для которой выполняется фильтрация.
					//"modelName": "Case",
// Колонка связанной модели, по которой осуществляется связь с основной моделью.
					//"assocProperty": "Id",
					//"operation": "Terrasoft.FilterOperations.Any",
					"compareType": "Terrasoft.ComparisonType.NotEqual",
					//"value": {
						//"value": "3e7f420c-f46b-1410-fc9a-0050ba5d6c38",
						//"displayValue": "Закрыт"}
                                        "value": "3e7f420c-f46b-1410-fc9a-0050ba5d6c38"
				},

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

"SyncFilter": {
					"type:" "Terrasoft.FilterTypes.Simple",
					"property": "SolutionOverdue",
					"compareType": "Terrasoft.ComparisonType.Equal",
					"value": true
				},

В данном случае ошибок при синхронизации не выдает, но и он тупо не работает.

Подскажите что не так делаю, и вообще объясните моменты с modelName и assocProperty - для чего эти значения. Коммент в примере совсем не понятно что имеется ввиду.

"Михайловский Илья Викторович" написал:

Извините за некропостинг, но может найдется кто, чтобы немного мне объяснить.

Мне надо отфильтровать Обращения, по состоянию, которые НЕ закрыты. Или хотя бы для примера SolutionOverdue, который означает "закрытие".

Добавляю в манифест фильтр:

"SyncFilter": {

                                        "property": "Status",

// Название модели, для которой выполняется фильтрация.

                                        //"modelName": "Case",

// Колонка связанной модели, по которой осуществляется связь с основной моделью.

                                        //"assocProperty": "Id",

                                        //"operation": "Terrasoft.FilterOperations.Any",

                                        "compareType": "Terrasoft.ComparisonType.NotEqual",

                                        //"value": {

                                                //"value": "3e7f420c-f46b-1410-fc9a-0050ba5d6c38",

                                                //"displayValue": "Закрыт"}

                                        "value": "3e7f420c-f46b-1410-fc9a-0050ba5d6c38"

                                },

Пробовал и так и сяк, при синхронизации в мобиле выдает ошибку.

Пробовал еще такой фильтр:

"SyncFilter": {

                                        "type:" "Terrasoft.FilterTypes.Simple",

                                        "property": "SolutionOverdue",

                                        "compareType": "Terrasoft.ComparisonType.Equal",

                                        "value": true

                                },

В данном случае ошибок при синхронизации не выдает, но и он тупо не работает.

Подскажите что не так делаю, и вообще объясните моменты с modelName и assocProperty - для чего эти значения. Коммент в примере совсем не понятно что имеется ввиду.

Добрый день, Илья!
Примеры описаны для версии 7.6.
Уточните, пожалуйста, версию системы и мобильного приложения.

"Вильшанский Дмитрий" написал:
Михайловский Илья Викторович пишет:

Извините за некропостинг, но может найдется кто, чтобы немного мне объяснить.

Мне надо отфильтровать Обращения, по состоянию, которые НЕ закрыты. Или хотя бы для примера SolutionOverdue, который означает "закрытие".

Добавляю в манифест фильтр:

"SyncFilter": {

                                        "property": "Status",

// Название модели, для которой выполняется фильтрация.

                                        //"modelName": "Case",

// Колонка связанной модели, по которой осуществляется связь с основной моделью.

                                        //"assocProperty": "Id",

                                        //"operation": "Terrasoft.FilterOperations.Any",

                                        "compareType": "Terrasoft.ComparisonType.NotEqual",

                                        //"value": {

                                                //"value": "3e7f420c-f46b-1410-fc9a-0050ba5d6c38",

                                                //"displayValue": "Закрыт"}

                                        "value": "3e7f420c-f46b-1410-fc9a-0050ba5d6c38"

                                },

Пробовал и так и сяк, при синхронизации в мобиле выдает ошибку.

Пробовал еще такой фильтр:

"SyncFilter": {

                                        "type:" "Terrasoft.FilterTypes.Simple",

                                        "property": "SolutionOverdue",

                                        "compareType": "Terrasoft.ComparisonType.Equal",

                                        "value": true

                                },

В данном случае ошибок при синхронизации не выдает, но и он тупо не работает.

Подскажите что не так делаю, и вообще объясните моменты с modelName и assocProperty - для чего эти значения. Коммент в примере совсем не понятно что имеется ввиду.

Добрый день, Илья!

Примеры описаны для версии 7.6.

Уточните, пожалуйста, версию системы и мобильного приложения.

7.8
Я уже сделал, но через модуль, создал с именем UsrMobileCaseModelConfig со след кодом:

Terrasoft.sdk.Module.addFilter("Case", Ext.create("Terrasoft.Filter", {
	name: "CaseCustomFilter",
	type: "Terrasoft.FilterTypes.Group",
	subfilters: [
//Не закрытый статус
		Ext.create("Terrasoft.Filter", {
			name: "CaseStatusFilterValue",
			property: "Status",
			compareType: Terrasoft.ComparisonTypes.NotEqual,
			value: "3e7f420c-f46b-1410-fc9a-0050ba5d6c38"
		}),
//Ответственный - текущий пользователь
		Ext.create("Terrasoft.Filter", {
			name: "CaseOwnerFilterValue",
			property: "Status",
			compareType: Terrasoft.ComparisonTypes.Equal,
			valueIsMacros: true,
			value: "Terrasoft.ValueMacros.CurrentUserContact"
		}),
//Не отменен статус
		Ext.create("Terrasoft.Filter", {
			name: "CaseStatusFilterValue",
			property: "Status",
			compareType: Terrasoft.ComparisonTypes.NotEqual,
			value: "6e5f4218-f46b-1410-fe9a-0050ba5d6c38"
		})
	]
}));

И затем в манифесте в PageExists подключил этот модуль, и все заработало.

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

Есть группа пользователей, которая входит в "Все сотрудники компании". Я удаляю права доступа этой группе на все записи раздела, а после выдаю права на некоторые записи (БП). Но пользователи этой группы видят все записи, т.к. права всех сотрудников не меняются. Как дать права на определенные записи раздела данной группе, не затрагивая права всех пользователей?

Нравится

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

Олег, права доступа раздаются как определенным пользователям, так и ролям с учетом того, что роль "Все сотрудники компании" включает в себя все роли и всех пользователей. В Вашем случае следует обращать внимание, как настроены права для роли "Все сотрудники компании" на этот объект. Если для них установлены полные права, то не зависимо от Вашего скрипта они будут видеть записи. Если же для роли "Все сотрудники компании" доступ запрещен, то создайте отдельную роль, включив туда нужный сотрудников и раздайте для этой роли права. Но в целом, нужно видеть полную картину Ваших настроек прав, чтоб понимать суть проблемы. Какие права для ролей стоят на права на записи, на права на операции и для каких ролей и пользователей в интересующем Вас разделе. Если требуется более детальная консультация непосредственно с рассмотрением прав доступа на Вашем сайте, то рекомендую перенести дальнейшее обсуждение вопроса в рамках технической поддержки.

Подскажите как добавить роль, которая не будет входить в "Все сотрудники компании"? И почему у меня может не быть возможности добавить роль в группу "Все пользователя портала"? Кнопки добавить организацию/подразделение не доступны (даже Supervisor'у).

Роль "Все сотрудники компании" является родительской, все остальные подчиненные. Но это не значит, что права будут применяться для всех сотрудников, если настраиваете их для определенной роли. Вы можете раздавать права нужной Вам роли, при этом это никак не отразится на роли "Все сотрудники компании". Что касается роли "Все пользователи портала", то данная роль работает только в продуктах линейки Service, так как портал есть только там.
Для большего понимания настройки прав доступа в системе рекомендую посмотреть видеоролик: https://www.youtube.com/watch?v=x5C6VcOhKj4&feature=youtu.be&list=PLDp-…

"Сафонов Олег" написал:Подскажите как добавить роль, которая не будет входить в "Все сотрудники компании"? И почему у меня может не быть возможности добавить роль в группу "Все пользователя портала"? Кнопки добавить организацию/подразделение не доступны (даже Supervisor'у).

Олег, добавить роль на одном уровне с "Все сотрудники компании" можно только инсертом в базу данных. Лучшим вариантом будет настройка механизма раздачи прав по умолчанию на записи + настройка БП, который бы изменял права по необходимости.

В портальных пользователях иерархия вообще отключена, поэтому добавить туда дочернюю группу не выйдет. Насколько мне известно, включение структуры портальных пользователей произовйдёт в одном из будущих релизов bpm'online.

Настраивал права и для подчиненной роли, входящей в "Все сотрудники компании", и на одном уровне со всеми сотрудниками (инсертом). Если открыть Действия - Настроить права доступа на карточке - там нету группы, для которой были настроены права, но записи все-равно видны.
Роли актуализировал. Галочка "Администрируется по записям" стоит.

Мне нужно чтоб права на записи настраивались конкретной группе пользователей. А занятие это бесполезное чуть более чем полностью, пока в правах конкретной записи указаны все сотрудники компании. Чтоб запись перестала видеть группа пользователей, необходимо убрать всех сотрудников, а их права должны остаться без изменений.

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

"Адасюк Валерий Викторович" написал:

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

Прикрепленный файлРазмер

skripty.zip
2.47 кб


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

Олег, данная ситуация требует индивидуального рассмотрения. Предлагаю перенести рассмотрение данного вопроса в рамках отдела технической поддержки, с последующим подключением к Вашему сайту. Напишите на адрес support@terrasoft.ru какие права, какой группе и на какой раздел нужно раздать, а также ссылку на сайт, о котором идет речь.

Здравствуйте! Интересует такой вопрос. Я сосдал учетную запись для Бухгалтера и в администрировании доступа к обекту выдал ему права на чтение Кто создает: Все сотрудники компании.
В итоге он начал видеть счета которые создаются после того как это правило создалось. Но мне нужно чтобы он видел и счета которые были раньше создани, а не только начиная с сегодня 16-30. Можна ли както сделать чтобы бухгалтер смог видеть счета например с 01.07.2016 не выдавая ему админские права на доступ.

Николай, добрый день.

Все администрируемые права доступа по записям применяются на записи созданные только после настройки прав доступа, о чем написано в документации (первый абзац) -

https://academy.terrasoft.ru/documents/sales-enterprise/7-8-0/detal-dos…

Для того, чтобы распространить права на существующие записи, есть несколько вариантов:

1) Запрос в БД - решается при помощи написания SQL-запроса
2) БП - бизнес-процесс, в котором есть элемент "Изменение прав доступа".

Детальнее о правах доступа Вы можете узнать здесь - https://www.youtube.com/watch?v=x5C6VcOhKj4&feature=youtu.be&list=PLDp-…

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

На карточке раздела "Заявки на товары и услуги" есть деталь "Товары/услуги", которая работает с развязочной таблицей. Реестр в детали редактируемый.
Если открыть карточку, обновить страницу с очисткой кеша, и перейти в другой раздел, или просто по ссылке из справочного поля - карточки других разделов загружаются с пустыми полями. Выдает ошибку: "Элемент коллекции с именем SxProdDemand не найден". Если я правильно понимаю, в запрос почему-то добавляются колонки из детали.
Если обновить страницу с очисткой кеша в другом разделе - все разделы и ссылки работают.
Если отключить редактируемый реестр - все работает.
Деталь в базе зарегистрирована. Карточки редактирования у детали нет.
В чем дело и как это исправить?

Нравится

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

Добавил страницы редактирования - проблема исчезла. Есть ли возможность не добавлять страницы, т.к. они не будут использоваться?

Здравствуйте, Олег!

Рекомендую Вам обратиться в службу технической поддержки bpm'online.
Т.к. данный вопрос требует доступа к БД и Сайту для анализа вопроса.

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

Доброго времени суток!
Стоит задача настраивать права доступа к записям раздела с помощью фильтров. Есть карточка со справочным полем "Раздел". Необходимо отобразить фильтры записей для данного раздела, чтобы в дальнейшем запускать БП, настраивающий права доступа к отфильтрованным записям выбранного раздела.
Вопроса два:
1. Как можно хранить значения фильтров в колонке, чтоб потом использовать в БП при раздаче прав?
2. Как отобразить фильтры на карточке? Я создаю контейнер по аналогии с BaseSection, и использую sandbox.loadModule. Ошибок не выдает, как и сами фильтры.

diff:

{
        "operation": "insert",
        "name": "ExtendedFiltersContainer",
        "parentName": "Header",
        "propertyName": "items",
        "values": {
                "id": "ExtendedFiltersContainer",
                "selectors": {"wrapEl": "#ExtendedFiltersContainer"},
                "itemType": Terrasoft.ViewItemType.CONTAINER,
                "controlConfig": {"visible": {"bindTo": "IsExtendedFiltersVisible"}},
                "wrapClass": ["extended-filters-container-wrapClass", "left-inner-el"],
                "items": []
        }
}

methods:
init: function() {
        this.callParent(arguments);
        var extendedFilterModuleId = this.getExtendedFilterEditModuleId();
        this.sandbox.loadModule("ExtendedFilterEditModuleV2", {
                renderTo: "ExtendedFiltersContainer",
                id: extendedFilterModuleId
        });
}

Нравится

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

Здравствуйте, на данный момент, в системе не предусмотрено возможности использовать контролл фильтрации, который вы видите на странице секции, на пользовательских страницах другого типа.
Но если Вам необходимо собирать информацию по примененным фильтрам в разделе, а потом их куда-нибудь сохранять, и использовать в тех или иных целях.
Вы можете добавить в действия секции кнопку, которую будет видеть, к примеру, только администратор системы. Перед нажатием применять к разделу фильтрацию. И использовать метод секции «getFilters()» который вернет вам объект, всех, примененных к разделу фильтров.
Как с ним поступать далее, решать вам.
Так же, объект фильтров поддерживает сериализацию и десереализацию.
Ниже прилагаю небольшой пример по получению и сериализации объекта всех примененных к разделу фильтров:

define("OrderSectionV2", [], function() {
	return {
		entitySchemaName: "Order",
		attributes: {
		},
		methods: {
			getSectionActions: function() {
				var actionMenuItems = this.callParent(arguments);
				actionMenuItems.addItem(this.getActionsMenuItem({
					"Type": "Terrasoft.MenuSeparator",
					"Caption": ""
				}));
				actionMenuItems.addItem(this.getActionsMenuItem({
					"Caption": "testFilters",
					"Click": { "bindTo": "testFilters" }
				}));
				return actionMenuItems;
			},
			testFilters: function() {
				var filters = this.getFilters();
 
				var serializationInfo = filters.getDefSerializationInfo();
				serializationInfo.serializeFilterManagerInfo = true;
 
				var filtersJson = filters.serialize(serializationInfo);
				this.Terrasoft.showInformation(filtersJson);
			}
		},
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		diff: /**SCHEMA_DIFF*/[]/**SCHEMA_DIFF*/
	};
});

простой вариант:

сделайте справочное поле со ссылкой на объект нужных вам фильтров (Например ContactFolder/Группа контакта) и выбирайте нужную группу. В таблицах групп есть поле SearchData - это сохраненные фильтры (для клиентской стороны). В базе они выглядят так:

Перед использованием их значение надо десериализовать:

Terrasoft.deserialize(SearchData)

После этого можете их добавлять в качестве фильтров в запросы:

var searchData = Terrasoft.deserialize(contactGroup.getByIndex(0).SearchData);
config.filters.addItem(searchData);
...
var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
	rootSchemaName: "Contact"
});
esq.addColumn("Id");
esq.filters = config.filters;
esq.getEntityCollection(function(result) {
	if (result.success) {
		...
	}
}, this);

Тут есть две проблемы:
1) Группа уже должна быть создана и настроена (это не всегда плохо, но в половине случаев - точно)
2) Если вам надо выбирать из фильтров разных разделов, надо будет сделать переключение схем в зависимости от выбранного раздела ,само справочное поле групп - виртуальным, а фильтры сохранять в другом, скрытом

Сложный вариант:
1) Сделать модуль для настройки и фильтров через интерфейс. Тут логичнее использовать схемы тех разделов, которые будут фильтроваться - так пользователь сможет сразу увидеть результат фильтрации. Но их придется обернуть в отдельный модуль, который будет загружать нужный раздел поверх открытой карточки и иметь как минимум две кнопки - ОК/Отмена - а из схемы раздела надо будет убрать весь остальной функционал (фактически - создать дочернюю схему, например, от contactSectionV2, на которой скрыть все ненужные элементы). Загружать модуль тогда надо будет как-то так

this.sandbox.loadModule("ReportModule", {
	id: reportModuleId,
	renderTo: this.renderTo,
	keepAlive: true
});

2) наладить сообщение между модулями карточки и только что созданным для обмена фильтрами

Еще один простой вариант:
создавать вашу сущность с того раздела, который собираетесь фильтровать - открываете Контакты, настраиваете фильтрацию в расширенном режиме и выбрав действие "Создать [Что-то]" передаете туда сразу и раздел и фильтры (их надо будет сначала считать в схеме раздела).

А вот как потом использовать эти фильтры в БП - это вопрос)).
Возможно будет проще сохранять не фильтрацию, а набор ИД отфильтрованных записей. Это простая деталь и в БП проблем особо не будет.

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

define("SfTestTeamDetail", ["terrasoft", "SfTestTeamDetailResources","ConfigurationConstants", "ConfigurationEnums", "SferaConstants", 'TestingConstants'],
	function(Terrasoft, resources, ConfigurationConstants, enums, SferaConstants, TestingConstants) {
		return {
            entitySchemaName: "SfTestTeam",
			attributes: {},
			messages: {
                "GetTargetGroup": {
                    mode: Terrasoft.MessageMode.PTP,
                    direction: Terrasoft.MessageDirectionType.PUBLISH
                }
            },
			methods: {
                initData: function() {
                    this.callParent(arguments);
                },
				onAddButtonClick: function() {
					var cardState = this.sandbox.publish("GetCardState", null, [this.sandbox.id]);
					var isNew = (cardState.state === enums.CardStateV2.ADD ||
					    cardState.state === enums.CardStateV2.COPY);
					if (isNew) {
						var args = {
							isSilent: true,
							messageTags: [this.sandbox.id]
						};
						this.sandbox.publish("SaveRecord", args, [this.sandbox.id]);
						return;
					}
                    var SfTargetGroup = this.sandbox.publish("GetTargetGroup", null, [this.sandbox.id]).targetGroup;
                    this.set("SfTargetGroup", SfTargetGroup);
                    this.addContactToTeam(arguments[3] === "byGroup");
				},
                addContactToTeam: function(byGroup) {
                    if(byGroup) {
                        this.getGroupList();
                    } else {
                        this.getUsersList();
                    }
                },
				getUsersList: function() {
					/*var scope = this;
					var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
						rootSchemaName: "SfTestTemplate"
					});
					esq.addColumn("SfTargetGroup");
					esq.filters.addItem(Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL, "Id", this.get("MasterRecordId")));
					esq.getEntityCollection(function(result) {
						if (result.success && result.collection.getItems().length>0) {
							scope.prepareLookupConfig(result.collection.getItems()[0].get("SfTargetGroup").value);
						}
					}, scope);*/
                    this.Terrasoft.chain(
                            function(next) {
                                this.getContactLookupConfig(function() {
                                    next();
                                }, this);
                            },
                            function(next) {
                                this.openContactLookup(function() {
                                    next();
                                }, this);
                            },
                            function(next) {
                                this.addContactCallback(function() {
                                    next();
                                }, this);
                            },
                            function(next) {
                                this.reloadGridData();
                                next();
                            },
                            this);
            	},
                getGroupList: function() {
                    this.Terrasoft.chain(
                            function(next) {
                                this.getContactGroupLookupConfig(function() {
                                    next();
                                }, this);
                            },
                            function(next) {
                                this.openContactGroupLookup(function() {
                                    next();
                                }, this);
                            },
                            function(next) {
                                this.getContactLookupConfig(function() {
                                    next();
                                }, this);
                            },
                            function(next) {
                                this.getSelectedGroupFilter(function() {
                                    next();
                                }, this);
                            },
                            function(next) {
                                this.getFilteredContacts(function() {
                                    next();
                                }, this);
                            },function(next) {
                                this.addContactCallback(function() {
                                    next();
                                }, this);
                            },
                            function(next) {
                                this.reloadGridData();
                                next();
                            },
                            this);
                },
                getContactLookupConfig: function(callback) {
                    var SfTargetGroup = this.get("SfTargetGroup");
                    var templateId = this.get("MasterRecordId");
                    var filters = this.Ext.create("Terrasoft.FilterGroup");
                    var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
                        rootSchemaName: "SfTestTeam",
                        isDistinct: true
                    });
                    var IdsExists = [];
                    esq.addColumn("SfContact");
                    esq.filters.addItem(Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL, "SfTestTemplate",
                            templateId));
                    esq.getEntityCollection(function(result) {
                        if (result.success) {
                            var items = result.collection.getItems();
                            IdsExists = [];
                            Terrasoft.each(items, function(item) {
                                var contact = item.get("SfContact");
                                if (contact && contact.value) {
                                    IdsExists.push(contact.value);
                                }
                            }, this);
                            var contactType = SferaConstants.ContactType.Employee.value;
                            if (SfTargetGroup && SfTargetGroup.value) {
                                if (SfTargetGroup.value == TestingConstants.TargetGroupType.Doctor.value) {
                                    contactType = SferaConstants.ContactType.Doctor.value;
                                }
                                filters.addItem(Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL, "Type",
                                        contactType));
                            }
                            filters.addItem(this.getNotExistsInTeamFilter(IdsExists));
                            var config = {
                                entitySchemaName: "Contact",
                                multiSelect: true,
                                columns: ["Name"],
                                hideActions: true
                            };
                            config.filters = filters;
                            this.set("ContactLookupConfig", config);
                            if (this.Ext.isFunction(callback)) {
                                callback.call(this);
                            }
                        }
                    }, this);
                },
                getContactGroupLookupConfig: function(callback) {
                    var config = {
                        entitySchemaName: "ContactFolder",
                        //multiSelect: true,
                        columns: ["Id", "Name", "SearchData"],
                        hideActions: true
                    };
                    var filters = this.Terrasoft.createFilterGroup();
 
                    //todo use configconstants
                    filters.addItem(Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL, "FolderType.Id",
                            "65ca0946-0084-4874-b117-c13199af3b95"));//dynamic
                    config.filters = filters;
                    this.set("ContactGroupLookupConfig", config);
                    if (this.Ext.isFunction(callback)) {
                        callback.call(this);
                    }
                },
                openContactGroupLookup: function(callback) {
                    var config = this.get("ContactGroupLookupConfig");
                    this.openLookup(config, function(args) {
                        this.set("SelectedContactGroup", args.selectedRows);
                        if (this.Ext.isFunction(callback)) {
                            callback.call(this);
                        }
                    }, this);
                },
                getSelectedGroupFilter: function(callback) {
                    // todo учеть возможность множественного выбора групп
                    var contactGroup = this.get("SelectedContactGroup");
                    var config = this.get("ContactLookupConfig");
                    if (contactGroup.getCount()) {
                        var searchData = Terrasoft.deserialize(contactGroup.getByIndex(0).SearchData);
                        config.filters.addItem(searchData);
                    }
                    this.set("ContactLookupConfig", config);
                    if (this.Ext.isFunction(callback)) {
                        callback.call(this);
                    }
                },
                openContactLookup: function(callback) {
                    var config = this.get("ContactLookupConfig");
                    this.openLookup(config, function(args) {
                        this.set("SelectedContacts", args.selectedRows);
                        if (this.Ext.isFunction(callback)) {
                            callback.call(this);
                        }
                    }, this);
				},
                getFilteredContacts: function(callback) {
                    var ContactLookupConfig = this.get("ContactLookupConfig");
                    var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
                        rootSchemaName: "Contact"
                    });
                    esq.addColumn("Id");
                    esq.filters = ContactLookupConfig.filters;
                    esq.getEntityCollection(function(result) {
                        if (result.success) {
                            //Ids = result.collection.getKeys();
 
                            this.set("SelectedContacts", result.collection);
                            if (this.Ext.isFunction(callback)) {
                                callback.call(this);
                            }
                        }
                    }, this);
                },
                addContactCallback: function(callback){
                    this.showBodyMask();
                    var Ids = this.get("SelectedContacts").getKeys();
                    var bq = this.Ext.create("Terrasoft.BatchQuery");
                    this.Terrasoft.each(Ids, function(item) {
                        var insert = this.Ext.create("Terrasoft.InsertQuery", {
                            rootSchemaName: "SfTestTeam"
                        });
                        var newGuid = Terrasoft.generateGUID();
                        var testTemplate = this.get("MasterRecordId");
                        insert.setParameterValue("Id", newGuid , Terrasoft.DataValueType.GUID);
                        insert.setParameterValue("SfContact", item, Terrasoft.DataValueType.GUID);
                        insert.setParameterValue("SfTestTemplate", testTemplate, Terrasoft.DataValueType.GUID);
                        bq.add(insert);
                    }, this);
                    bq.execute(function(result) {
                        this.hideBodyMask();
                        if (result.success) {
                            if (this.Ext.isFunction(callback)) {
                                callback.call(this);
                            }
                        } else console.log("error");
                    }, this);
                },
                getNotExistsInTeamFilter: function(IdsExists) {
                    var existsFilter = this.Terrasoft.createNotExistsFilter("[SfTestTeam:SfContact:Id].SfContact");
                    var subFilter = this.Terrasoft.createColumnInFilterWithParameters(
                            "SfContact", IdsExists);
                    existsFilter.subFilters.addItem(subFilter);
                    existsFilter.subFilters.addItem(Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL, "SfTestTemplate",
                            this.get("MasterRecordId")));
                    return existsFilter;
                }
            },
			diff: /**SCHEMA_DIFF*/[
 
				{
					"operation": "merge",
					"name": "AddRecordButton",
					"values": {
						"click": {bindTo: "onAddButtonClick"},
						"menu": [],
                        "tag": "single"
					}
				},
				{
					"operation": "remove",
					"name": "AddTypedRecordButton"
				},
				{
					"operation": "remove",
					"name": "EditRecordMenu"
				},
				{
					"operation": "remove",
					"name": "CopyRecordMenu"
				},
                {
                    "operation": "insert",
                    "name": "AddTargetGroupButton",
                    "parentName": "Detail",
                    "propertyName": "tools",
                    "values": {
                        "itemType": Terrasoft.ViewItemType.BUTTON,
                        "click": {"bindTo": "onAddButtonClick"},
                        "style": Terrasoft.controls.ButtonEnums.style.TRANSPARENT,
                        "caption": {"bindTo": "Resources.Strings.AddTargetGroupButtonCaption"},
                        "tag": "byGroup"
                    },
                    "index": 1
                }
			]/**SCHEMA_DIFF*/
		};
	}
);

https://youtu.be/WlaJY3duIJY

Спасибо огромное за идеи! Взяв понемногу из каждой у меня нарисовался алгоритм.
Остается только одна задача: я передаю в бп коллекцию записей, UId схемы раздела и объект администрирования. Не могу понять как правильно их передать в элемент "изменить права доступа". Подскажите, пожалуйста, как правильно реализовать данную задачу.

Уже не нужно, разобрался

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

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

Версия 7.7.0.2293

В Terrasoft.WebApp\DesktopBin\WorkspaceConsole\ запустил PrepareWorkspaceConsole.x64.bat
Прописал правильный connectionStrings в Terrasoft.Tools.WorkspaceConsole.exe.config
Выгружаю изменения командой Terrasoft.Tools.WorkspaceConsole.exe -operation=SaveDBContent -workspaceName=Default -destinationPath=D:\Work\Repository\Kapital -contentTypes=Repository

Появляется ошибка "Object reference not set to an instance of an object" (скрин во вложение)

Конфигурация успешно компилируется. Сайт работает.

Подскажите как узнать в какой схеме проблема и что нужно сделать что бы ее исправить?

Нравится

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

Добрый день.
Возможно не все файлы скопировались из папки
..\Terrasoft.WebApp\bin
Попробуйте скопировать в ручном режиме с полной заменой

"Пащенко Александр Сергеевич" написал:

Добрый день.

Возможно не все файлы скопировались из папки

..\Terrasoft.WebApp\bin

Попробуйте скопировать в ручном режиме с полной заменой

Скопировал все вручную, не помогло.

Здравствуйте.
В "Terrasoft.Tools.WorkspaceConsole.exe.config" кроме самих настроек подключения есть секция параметров:

<db>
      <general connectionStringName="mssqlDB" securityEngineType="Terrasoft.DB.MSSql.MSSqlSecurityEngine, Terrasoft.DB.MSSql" executorType="Terrasoft.DB.MSSql.MSSqlExecutor, Terrasoft.DB.MSSql" engineType="Terrasoft.DB.MSSql.MSSqlEngine, Terrasoft.DB.MSSql" metaEngineType="Terrasoft.DB.MSSql.MSSqlMetaEngine, Terrasoft.DB.MSSql" metaScriptType="Terrasoft.DB.MSSql.MSSqlMetaScript, Terrasoft.DB.MSSql" typeConverterType="Terrasoft.DB.MSSql.MSSqlTypeConverter, Terrasoft.DB.MSSql" binaryPackageSize="1048576" currentSchemaName="dbo" />
    </db>

там есть параметр general connectionStringName="mssqlDB" . Параметры подключения должны быть указаны именно в этой connectionString (кроме mssqlDB ещё есть db).

Александр, спасибо.

Помогло.

"Котенко Александр" написал:Параметры подключения должны быть указаны именно в этой connectionString

либо можно использовать
"инструкция)" написал:
webApplicationPath
Путь к веб-приложению, из которого будет вычитана информация по соединению с БД
Необязательный параметр. Если не указан - соединение будет установлено с базой, указанной в строке соединения в файле App.Config приложения. Если указан - соединение будет установлено с БД из файла ConnectionStrings.config веб-приложения
Используется для всех операций, в которых участвует БД

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

Доброго времени суток!
Есть развязочная таблица товаров и статей. Справочное поле "Статья" фильтруется по выбранному товару, на основе развязочной таблицы. Каким образом можно вывести все статьи, если в развязочной таблице отсутствуют записи по выбранному товару?

Нравится

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

Олег, здравствуйте!

Пример фильтрации можете посмотреть в схеме «ActivityPageV2» фильтр по колонке «Result».

Спасибо!

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