Добрый день!
На днях клиент выразил недоумение по поводу того, что невозможно нигде увидеть результирующие права того или иного пользователя в Terrasoft 3.x. Кроме того, Юлей Старун выдвигалась уже идея добавить этот функционал в базовую версию (см. http://www.community.terrasoft.ua/ideas/4744).

Поэтому решено было сделать такую опцию, которую каждый желающий может загрузить, если действительно это нужно.
Для этого нужно:
1. Загрузить сервисы из прикрепленного архива.
Внимание!Если Вами ранее вносились изменения в сервис sq_AdminUnit и Ваша версия этого сервиса наверняка отличается от базовой, то просто добавьте в этот Select Query фильтр по полю IsGroup и параметр IsGroup (тип - булевский).

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

function amiFullRightsOnExecute(ActionMenuItem, Sender) {
    GetFullRightsByAdminUnit(dlData.Dataset('ID'), dlData.Dataset('Name'));
}

Кроме того, нужно в скрипте этого окна подключить скрипт scr_AdminUnitFullRights.

3. Если необходимо показывать полные права и для групп пользователей, то необходимо проделать шаг 2, но уже для окна wnd_UsersGroups.

В результате в разделе "Администрирование" при выборе соотв. пункта меню Вы должны увидеть что-то типа этого:

В принципе, все. Пользуйтесь на здоровье. :)

Нравится

Поделиться

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

Стас, большое спасибо! Очень нужная и ценная доработка!

+1, очень полезная функция!

Был маленький недочет, который виден на скриншоте :), но я его уже подправил и прикрепил архив снова.

то что надо :)

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

Здравствуйте.
Есть ли в XRM возможность менять права доступа на запись при попадании её в динамическую группу?
Мне нужно реализовать следующее:
Если поле "Статус" = "Завершено", то ограничивать доступ.

Нравится

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

Штатно нет такой возможности.
Чтоб быстро работало - сделать хранимую процедуру для изменения прав доступа и вызывать ее на AfterPost нужного датасета.

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

Если поле Статус = Завершено, то делать карточку нередактируемой для всех, кроме супервизора. Для этого на OnPrepare карточки вызывать функцию

IsArchive(DataFieldName, DataFieldValue);

где
DataFieldName - название поля Завершено в датасете
DataFieldValue - значение, при котором карточка доступна только на чтение

Тело функции:

function IsArchive(DataFieldName, DataFieldValue){
 
	if((!Connector.CurrentUser.IsAdmin)
			&&(dlData.Dataset(DataFieldName) == DataFieldValue)){
		SetIsReadOnlyFields(Self, false);
		return;
	}
		SetIsReadOnlyFields(Self, true);
}
 
function SetIsReadOnlyFields(Window, IsEnabled) {
     for (var i = 0; i < Window.ComponentCount; i++) {
         var DataField = Window.Components(i).DataField;
         if(!IsUndefined(DataField)) {
                 var Component = Window.Components(i);
                 Component.IsEnabled = IsEnabled;
         } 
     }
} 

Спасибо за ответы.
А можно сделать то же самое с обычными группами? (Если, например, пользователи будут сами перемещать заявки в группу "Архив")

Тут будет нужно немного больше кода. Физически информация о вхождении контрагента в группу находится в ds_AccountInGroup.

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

function IsArchive(GroupID){
	var dsAccInGroup = Services.GetSingleItemByUSI('ds_AccountInGroup'); 
 
//проверяем вхождение в группу
	ApplyDatasetFilter(dsAccInGroup, 'AccountID', dlData.Dataset('ID'),true); 
 
//фильтруем по текущей записи в карточке
	ApplyDatasetFilter(dsAccInGroup, 'GroupID', GroupID,true);	
 
         if((!Connector.CurrentUser.IsAdmin)
                         &&(IsEmptyValue(dsAccInGroup)){   //если датасет пустой - не входит в группу
                 SetIsReadOnlyFields(Self, false);
                 return;
         }
                 SetIsReadOnlyFields(Self, true);
}

Естественно, вызывать функцию тогда нужно будет так:

IsArchive('{6BC9D098-6C64-4142-8615-321E56714C2F}');

Почему бы не воспользоваться стандартным функционалом "Запрет изменения данных"?

Такой вопрос ещё возник.
Допустим, есть запись, на которую ни у кого нет прав.
И есть группа, на которую у всех есть полный доступ.
Если запись переносится в эту группу, она всё равно остаётся без доступа?
Получается, права доступа группы - это права изменять собственно группу, а не записи в ней?

Здравствуйте, Ринат.

Да, Вы абсолютно правы. Права на группы раздаются отдельно, на записи - отдельно.

Запись будет видна пользователю (безразлично, в группе или нет) только если у пользователя есть права на эту запись.

Права доступа на группы регулируют только видимость самих групп в дереве групп слева.

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

Ситуация в следующем:
В администрировании создал Группы (Школы). Установил им определенные права, т.е. если Школа 1 добавила Студента 1, то Школа 2 не видит данного студента. Аналогично если Школа 2 добавила Студента 2, то Школа 1 не видит его. Примерно выглядит следующим образом:

1

Теперь на карточке дублей необходимо создать кнопку "Использовать найденного клиента". Описание:
Школа 1 добавила Студента 3 к себе. Теперь Студент 3 хочет учиться, помимо Школы 1, и в Школе 2. Школа 2 добавляет его к себе. Всплывает окно дублей о том что студент уже есть в базе. Суть кнопки в следующем: берется найденный студент и добавляется в Школу 2, но чтобы он был виден и Школе 1 и в Школе 2. Примерно отобразил следующем рисунке:

2

Как я понимаю необходимо в деталь "Доступ" (по найденному дублю записи) добавить запись с Группой (Школой). Встает встречный вопрос: как узнать Группу (Школу) в которой находится входящий в систему пользователь. Есть ли такая табличка с пользователем и соответствующей ему Группе (Школе)?

Нашел способ. Итак что я сделал:

В скрипте scr_SubjectDuplicates создал глобальную переменную

var DublFlag = 0;

На OnPrepare окна дублей добавил следующее

        var Sender = Self.Attributes('DuplicatesProperties').NotifyObject;

        if (Sender.Name == 'wnd_ContactEdit') {
                DublContact = 1
        }

и на кнопку "Использовать найденного клиента"

function btnUseOnClick(Control) {
        if (DublContact == 1) {
                var ds_Contact = Services.GetNewItemByUSI('ds_Contact');
                var Sender = Self.Attributes('DuplicatesProperties').NotifyObject;
                var ID = Self.Attributes('DuplicatesProperties').SubjectDataset.Values('ID');
                SetAttribute(Sender, 'IDRecord', ID);
                SetAttribute(Sender, 'IsDuble', true);
                btnUse.ModalResult = wmrCancel;
        }
}

Далее на кнопк Ок

function btnOKOnClick(Control) {
        var Dataset = dlData.Dataset;
        if (!CheckContactData(Dataset)) {
                return;
        }
        CheckName();
        CheckEmail();
        SetAttribute(Self, 'IDTo', dlData.Dataset.Values('ID'));
        scr_BaseDBEdit.btnOKOnClick(Control);
       
        // Если есть дубль но вызов функции
        var IsDuble = Self.Attributes('IsDuble');
        if (IsDuble == true) {  
                FillDublicatesValues();
                Self.Close();
        }

}

где в функции FillDublicatesValues()

взял Connector.CurrentUser.ContactID входящего.
Отфильтровал tbl_AdminUnit, поле UserContactID по Connector.CurrentUser.ContactID.
Взял ID

Отфильтровал tbl_UserInGroup, поле UserID по ID.
Взял все GroupID и вставил в tbl_ContactRight, т.е.

function FillDublicatesValues() {  
        var ID = Self.Attributes('IDRecord');
        var ds_Contact = Services.GetNewItemByUSI('ds_Contact');
        var DatasetTo = Services.GetNewItemByUSI('ds_Contact');
        var IDTo = Self.Attributes('IDTo');
       
        var ds_ContactRight = Services.GetNewItemByUSI('ds_ContactRight');
        ApplyDatasetFilter(ds_ContactRight, 'RecordID', ID, true)
        ds_ContactRight.Open();

                var IncomingUser = Connector.CurrentUser.ContactID;
                var ds_AdminUnit = Services.GetNewItemByUSI('ds_AdminUnit');
                ApplyDatasetFilter(ds_AdminUnit, 'UserContactID', IncomingUser, true);
                ds_AdminUnit.Open();
                var AdminUnitID = ds_AdminUnit.Values('ID');

                var ds_UserInGroup = Services.GetNewItemByUSI('ds_UserInGroup');
                ApplyDatasetFilter(ds_UserInGroup, 'UserID', AdminUnitID, true);
                ds_UserInGroup.Open();
                while (!ds_UserInGroup.IsEOF) {
                        var GroupID = ds_UserInGroup.Values('GroupID');
                       
                        while (!ds_ContactRight.IsEOF) {
                                if(ds_ContactRight.Values('AdminUnitID') != GroupID) {
                                        var IQ = Services.GetNewItemByUSI('iq_ContactRight');
                                        IQ.ColumnsValues.ItemsByName('ID').Value = Connector.GenGUID();
                                        IQ.ColumnsValues.ItemsByName('RecordID').Value = ID;
                                        IQ.ColumnsValues.ItemsByName('AdminUnitID').Value = GroupID;
                                        IQ.ColumnsValues.ItemsByName('CanRead').Value = true;
                                        IQ.ColumnsValues.ItemsByName('CanWrite').Value = true;
                                        IQ.ColumnsValues.ItemsByName('CanDelete').Value = false;
                                        IQ.ColumnsValues.ItemsByName('CanChangeAccess').Value = false;
                                        IQ.Execute();  
                                }
                                ds_ContactRight.GoToNext();
                        }
                        ds_UserInGroup.GoToNext();
                }
}

Думаю кому то в будущем пригодится!

Нравится

Поделиться

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

Подскажите кто как решает задачу, когда по БП под пользователем (например, после отработки задачи), который не имеет прав на изменение записи документа, нужно изменить состояние документа?

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

Нравится

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

можно сделать грязный хак - изменение делать через customsql и таблице в бд дать права доступа - на видимости в системе это не отобразится и, имхо, пользователь не проведай и сим разрешением воспользоваться не сможет

Здравствуйте, Андрей!

А если попробовать таким образом, в одном элементе процесса последовательно выполнять 3 действия:

1. вызывать хранимую процедуру, которая раздаст права текущему пользователю на изменение данного документа;
2. вызывать событие изменения поля состояния документа и сохранять его;
3. вызывать хранимую процедуру, которая забирает у пользователя право на изменение этого документа.

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

Инна Безверхняя,
II линия службы поддержки Terrasoft

Спасибо Алексей и Инна за ответ.
Инна, мне ваш совет кажется наиболее подходящим, действительно - можно доработать элемент БП чтения/записи данных так, как вы написали.

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

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
 
-- Процедура выполняет проставление указанного доступа на запись пользователю
 
create procedure [dbo].[tsp_SetRightsToRecordForUser]
	@TableName nvarchar(100),
	@RecordID uniqueidentifier,
	@OwnerID uniqueidentifier,
	@AccessFieldName nvarchar(20),
	@AccessFieldValue int
WITH EXECUTE AS OWNER
as
begin
	set nocount on
	if @TableName is Null OR
		@RecordID is NULL OR
		@OwnerID is Null OR
		@AccessFieldName is NULL OR
		@AccessFieldValue is NULL
		return
	declare @AuOwnerID uniqueidentifier
	SET @AuOwnerID = (SELECT ID FROM tbl_AdminUnit WHERE UserContactID = @OwnerID)
	IF @AuOwnerID IS NULL return
	begin tran
		execute ('update '+@TableName+' set '+@AccessFieldName+'='+@AccessFieldValue+
			' where RecordID='''+@RecordID+''' and AdminUnitID='''+@AuOwnerID+'''')		
		if @@rowcount = 0
		begin
			declare @CanRead int
			declare @CanWrite int
			declare @CanDelete int
			declare @CanChangeAccess int
			set @CanRead = 0
			set @CanWrite = 0
			set @CanDelete = 0
			set @CanChangeAccess = 0
			execute('set @'+@AccessFieldName+'='+@AccessFieldValue);
			execute ('insert into '+@TableName+' (ID, RecordID, AdminUnitID, CanRead, CanWrite, CanDelete, CanChangeAccess)'+
				'VALUES (NewID(), @RecordID, @AuOwnerID, @CanRead, @CanWrite, @CanDelete, @CanChangeAccess)')
		end
	commit tran
end

Также, я дал доступ всем пользователям на выполнение этой хранимой процедуры:

GRANT execute ON [dbo].[tsp_SetRightsToRecordForUser] TO public

Пример вызова хранимой процедуры:

EXEC [dbo].[tsp_SetRightsToRecordForUser] 'tbl_AccountRight', '{00DF9C8D-9399-49BE-89A8-078DB4CA093F}', '{251FB9AC-C17E-4DF7-A0CB-D591FDB97462}', 'CanRead', 1

У меня почему-то возникают следующие ошибки:

Msg 137, Level 15, State 1, Line 1
Must declare the scalar variable "@CanRead".
Msg 137, Level 15, State 2, Line 1
Must declare the scalar variable "@RecordID".

Может кто знает в чем проблема?

Здравствуйте, Андрей.

Скорее всего, проблема в том, что вот этот execute

execute ('insert into '+@TableName+' (ID, RecordID, AdminUnitID, CanRead, CanWrite, CanDelete, CanChangeAccess)'+
                                'VALUES (NewID(), @RecordID, @AuOwnerID, @CanRead, @CanWrite, @CanDelete, @CanChangeAccess)')

не видит параметров @CanRead, @CanWrite, @CanDelete, @CanChangeAccess.
Попробуйте сделать следующим образом:

execute ('insert into '+@TableName+' (ID, RecordID, AdminUnitID, CanRead, CanWrite, CanDelete, CanChangeAccess)'+
                                 'VALUES (NewID(), '''+@RecordID+''', '''+@AuOwnerID+''', 0, 0, 0, 0)')

Инна Безверхняя,
II линия службы поддержки Terrasoft

Переделал вот так и заработало без ошибок:

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
 
 
CREATE procedure [dbo].[tsp_SetRightsToRecordForUser]
	@TableName nvarchar(100),
	@RecordID uniqueidentifier,
	@OwnerID uniqueidentifier,
	@AccessFieldName nvarchar(20),
	@AccessFieldValue int
WITH EXECUTE AS OWNER
as
begin
	set nocount on
	if @TableName is Null OR
		@RecordID is NULL OR
		@OwnerID is Null OR
		@AccessFieldName is NULL OR
		@AccessFieldValue is NULL
		return
	declare @AuOwnerID uniqueidentifier
	SET @AuOwnerID = (SELECT ID FROM tbl_AdminUnit WHERE UserContactID = @OwnerID)
	IF @AuOwnerID IS NULL return
	begin tran
		execute ('update '+@TableName+' set '+@AccessFieldName+'='+@AccessFieldValue+
			' where RecordID='''+@RecordID+''' and AdminUnitID='''+@AuOwnerID+'''')		
		if @@rowcount = 0
		begin
			if @AccessFieldName = 'CanRead'
			execute ('insert into '+@TableName+' (ID, RecordID, AdminUnitID, CanRead, CanWrite, CanDelete, CanChangeAccess)'+
				'VALUES (NewID(), '''+@RecordID+''', '''+@AuOwnerID+''', '+@AccessFieldValue+', 0, 0, 0)')
			if @AccessFieldName = 'CanWrite'
			execute ('insert into '+@TableName+' (ID, RecordID, AdminUnitID, CanRead, CanWrite, CanDelete, CanChangeAccess)'+
				'VALUES (NewID(), '''+@RecordID+''', '''+@AuOwnerID+''', 0,'+@AccessFieldValue+', 0, 0)')
			if @AccessFieldName = 'CanDelete'
			execute ('insert into '+@TableName+' (ID, RecordID, AdminUnitID, CanRead, CanWrite, CanDelete, CanChangeAccess)'+
				'VALUES (NewID(), '''+@RecordID+''', '''+@AuOwnerID+''', 0, 0, '+@AccessFieldValue+', 0)')
			if @AccessFieldName = 'CanChangeAccess'
			execute ('insert into '+@TableName+' (ID, RecordID, AdminUnitID, CanRead, CanWrite, CanDelete, CanChangeAccess)'+
				'VALUES (NewID(), '''+@RecordID+''', '''+@AuOwnerID+''', 0, 0, 0, '+@AccessFieldValue+')')
 
		end
	commit tran
end
Показать все комментарии

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

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

Насколько понимаю, это задача должна решаться через скрипты на сохранении контрагента.

Куда и какой скрипт правльно вставить?

Нравится

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

При сохранении записи необходимо проверять изменение типа - событие AfterPost текущего Dataset, а для самой вставки записей прав лучше использовать процедуры СУБД.

"Кулак Олег" написал:

Спасибо, Олег.
а можете прокомментировать, почему лучше напрямую в бд выставлять права, а не через скрипты (например, ProcessGiveRecordRightsToContact)?

А можно ли как-то получить список всех ID-ок пользователей, входящих в определенную группу?

"Уймин Андрей" написал:А можно ли как-то получить список всех ID-ок пользователей, входящих в определенную группу?

Попробуйте для этой цели использовать сервис sq_UserInGroup

"Уймин Андрей" написал:Спасибо, Олег.
а можете прокомментировать, почему лучше напрямую в бд выставлять права, а не через скрипты (например, ProcessGiveRecordRightsToContact)?

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

"Уймин Андрей" написал:А можно ли как-то получить список всех ID-ок пользователей, входящих в определенную группу?

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

"Раловец Ольга" написал:лучше раздавать права группе пользователей

Полностью согласен с Ольгой. Это единственно верный вариант.

да, точно, конечно же, надо на группы! спасибо, Ольга и Олег!

а скрипты (в частности AfterPost) можно выполнять только под правами текущего пользователя?

Не сами скрипты. В скриптах Вы будете добавлять записи в таблицу прав с помощью сервисов DataSet, SelectQuery, а они при формировании запроса на вставку позаботятся о том, чтобы эти запросы выполнялись от имени текущего пользователя и соответственно с его правами. Это реализовано в ядре и изменить нельзя. А хранимые процедуры по умолчанию выполняются от имени создателя, следовательно будут пользоваться правами того, под кем Вы создавали хранимку, и поэтому Вы наверняка будете создавать ее под пользователем с максимальными правами, иначе с процедурой раздачи прав могут быть проблемы.

"Раловец Ольга" написал:

небольшое уточнение: а если создается новая запись, то у создающего пользователя ведь гарантированно будет право на добавление прав?

"Уймин Андрей" написал:ебольшое уточнение: а если создается новая запись, то у создающего пользователя ведь гарантированно будет право на добавление прав?

Да

Ну а если пользователь, у которого есть право на изменение контрагента, но нет права на изменение прав на эту запись, изменит тип контрагента и сохранит запись, то ваш скрипт перераздать права не сможет

да, понятно
спасибо!

Я бы рекомендовал для таких целей использовать триггеры. Хранимая процедура, которая может выдавать права и доступна для выполнения любому пользователю - это нормальная дыра в безопасности.

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

Интересно как кто справляется с тем, что при запрете доступа к таблице для какого-то пользователя также нельзя достучаться к записям этой таблицы из скрипта?

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

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

Нравится

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

Можно хранимку вызывать в скрипте где под полноправным/нужным пользователем все считается/запрашивается. А права настраивать в системе как обычно

"Александр Кудряшов" написал:Можно хранимку вызывать в скрипте где под полноправным/нужным пользователем все считается/запрашивается. А права настраивать в системе как обычно

Это нужно ее вызывать перед каждым считыванием/записью/удалением данных из скрипта?

Уточню - именно в ней будут запросы, производящие нужную обработку данных - несколько штук написать под каждое из действий. Исполнение их идет напрямую на сервере в обход системы безопасности - не совсем красиво но сработает

"Кошкаров Андрей" написал:Это нужно ее вызывать перед каждым считыванием/записью/удалением данных из скрипта?

Мне кажется, в данном случае совсем наоборот: все операции выполняются согласно настроенных прав доступа, а хранимую процедуру нужно выполнять только для подсчёта остатков на складах.

Конечно же, если пересчёт должен происходить автоматически при каждом выполнении операций над накладными, нужно вызывать эту хранимку. Но думаю, её можно написать универсальным образом.

Есть еще один вариант.
Делаете View в котором у вас построены суммы по Group by или просто по Sum. Далее мапите ее на сервис Table, а далее по классике. Чем это удобно, тем что вы сможете сделать джоин со своей основной таблице и вам не надо ничего вызывать дополнительно. Но тут может быть побочный эффект - это производительность.
Способ не идеален, но у вас всегда будут актуальные суммы.

А есть какой-то пример такой процедуры, которая бы у указанной таблицы устанавливала права для текущего пользователя?

Например, так:

CREATE procedure [dbo].[tsp_SetRightsForTable] (
	@TableName nvarchar(100), @CanRead int, @CanWrite int, @CanDelete int, @CanChange int)
as
 
declare @Text nvarchar(4000)
set @Text = '
declare	@RecordID as uniqueidentifier,
	@AdmUnitID as uniqueidentifier
 
declare c_RecordsForUpdate cursor for
  select [' + @TableName + '].[ID] from [' + @TableName +']
 
open c_RecordsForUpdate
  while 1 = 1    
  begin
    fetch c_RecordsForUpdate
      into @RecordID
    if @@fetch_status = -1 break 
    if @@fetch_status = -2 continue
       set @AdmUnitID = (SELECT [ID]
  FROM [dbo].[tbl_AdminUnit]
  WHERE [SQLObjectName] = SYSTEM_USER)
 
  if @AdmUnitID is NULL break
 
	 if not exists (select [ID] from [' + @TableName + 'Right] where [RecordID] = cast(@RecordID as varchar(38)) and [AdminUnitID] = cast(@AdmUnitID as varchar(38)))
	 insert into [' + @TableName + 'Right]
        	(RecordID, AdminUnitID, CanRead, CanWrite, CanDelete, CanChangeAccess)
        	values (cast(@RecordID as varchar(38)), cast(@AdmUnitID as varchar(38)), ' + cast(@CanRead as varchar(1)) + ', ' +
	cast(@CanWrite as varchar(1)) + ', ' + cast(@CanDelete as varchar(1)) + ', ' + cast(@CanChange as varchar(1)) + ')
    end
close c_RecordsForUpdate
deallocate c_RecordsForUpdate'
 
exec sp_executesql @Text
GO

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

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

Есть задача- убрать контроль доступа на чтение на записи отображаемые в детали История раздела Контрагента. Но сохранить контроль доступа при попытку перейти к записи по действию Перейти к '.....' в контекстном меню.
Это требуется для того, чтоб пользователи у которых есть доступ на чтение к Разделам, но нет прав на чтение конкретной записи могли видеть, когда и кто общался с этим клиентом и т.д. Но не могли просмотреть эту информацию более детально.

Нравится

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

Реализовать подобное возможно, например, одним из двух следующих вариантов:

а) создать хранимую процедуру, которая будет возвращать полную историю по контрагенту, дать пользователю доступ на её выполнение, создать запрос, который будет содержать результат выполнения процедуры (подробнее здесь), и этот запрос использовать в детали;

б) взять текст запроса из предпросмотра sq_AccountHistory, создать представление, используя этот текст, раздать пользователю на него доступ, а перед открытием датасета ds_AccountHistory заменять текст его запроса на выборку из этого представления (например, SELECT ID, Name, CreatedOn, RecordType FROM vw_FullAccountHistory). При этом не забыть о фильтрации по AccountID.

Решил остановиться на втором варианте, а есть где в системе посмотреть реализацию подобного функционала?
Я так думаю, надо писать скрипт на событии OnDatasetAfterOpen, где менять текст запроса? Я так никогда не делал, можно пример.
Или просто сделать альтернативный сервис sq_AccountHictory с текстом запроса типа

SELECT ID, Name, CreatedOn, RecordType FROM vw_FullAccountHistory

и заменить в параметрах датасета "Запрос на выборку" с текущего запроса на альтернативный?

Да, можно сделать, как Вы предложили. Для этого Вам необходимо будет после создания представления создать сервис таблицы с названием vw_FullAccountHistory и добавить в него колонки из представления, не обновляя структуру базы данных (то есть, сам сервис сохранить, но на вопрос, обновить ли элемент в базе данных, ответить "Нет"). Только после этого Вы сможете создать SelectQuery с таким текстом (представление появится в списке таблиц).

В базовой версии подобных примеров нет.

"Лабьяк Олег Игоревич" написал:При этом не забыть о фильтрации по AccountID.

А как это сделать, я не могу понять :-(. В сервисе sq_FullAccountHistory есть параметр AccountID, а как его связать с ID записи из основного окна Раздела Контрагенты?

Можно вынести поле AccountID в представление vw_FullAccountHistory. А в запросе, который будете создавать на основе представления, создать фильтр по AccountID, а саму колонку в запросе не выбирать. При этом нужно учесть, что поле AccountID необходимо добавить в каждый UNION представления.

Спасибо большое за консультацию все получилось. Я сделал, как Вы рекомендовали -
1. Добавил колонку AccountID в Представление, соответственно добавив ее в основной запрос(формирующий Представление) и все union'ы для извлечения Контрагента из документов отображаемых на детали история.
2. Добавил ее в табличном сервисе vw_FullAccountHistory
3. В сервисе Query в разделе Where добавил фильтр, где и связал Параметр AccountID и колонку из сервиса vw_FullAccountHistory с таким же названием и все стало фильтроваться на детали.
Но можно еще один вопрос на последок в целях, так сказать дальнейшего самообразования. :-)
По умолчанию у меня когда я делал фильтр в секции Where В сервисе Query, он естественно был активен(был установлен флаг), а в исходном сервисе sq_AccountHistory все фильтры в основном запросе и union'ах были отключены... т.е. флаг был снят, но на детали все равно происходила фильтрация, вопрос почему?

Руслан, данный фильтр включается автоматически при обновлении детали. Это прописано в скрипте раздела (scr_AccountsWorkspace) при вызове функции RefreshAccountsHistoryDetail.

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

Спасибо, за разъяснения.

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

При попытке получения значения OwnerID таблицы контактов выскакивает ошибка:
Ошибка выполнения метода 'ButtonOnClick'. Поле 'Ответственный' не активно «Call Stack»

При нажатии на кнопку на панели реестра записей раздела "Контрагенты" вызывается следующая функция, в которой выскакивает эта ошибка:

function ButtonOnClick(Control) {
var Dataset = dlData.Dataset
var RecordID = Dataset.Values('ID');
var OwnerID = Dataset.Values('OwnerID');
ShowWarningDialog(OwnerID);    
var AccessDataset = GetItemRightsDatasetByItemDataset(Dataset)
UpdateParentItemRights(RecordID, OwnerID, AccessDataset, true);
}

Эта функция выполняет добавление права доступа на выделенную запись реестра записей раздела Контрагента для Ответсвенного в выбранной записи. Он должен потом появиться на детали "Доступ".

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

Нравится

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

"Кошкаров Андрей Викторович" написал:var OwnerID = Dataset.Values('OwnerID');

вот это заменяете на

var OwnerID = GetFieldValueFromDisabledField(Dataset, 'OwnerID');

функция GetFieldValueFromDisabledField описана в scr_DB

ООО "Лайнсервис"
www.ls-crm.ru

Дело в том, что список полей запросе для датасета создается динамически, в реестре у Вас, очевидно, не отображается значение поля "Ответственный", поэтому сформированный датасет просто не содержит этого поля ("Поле 'Ответственный' не активно"). Кстати, если в запросе данного датасета для OwnerID поставить галку "Всегда выбирать в запросе" то можно пользоваться будет и методом Dataset.Values('OwnerID'), так как поле будет включаться в запрос всегда

[update] если из контрагентов обращение идет - тогда либо, как верно заметил Сергей, поставить галку в sq_Contact, либо пользоваться упомянутой мной функцией

ООО "Лайнсервис"
www.ls-crm.ru

Проверьте чтобы в запросе на поле OwnerID было активно свойство "всегда выбирать в запросе".

Спасибо за помощь. Заработало :)

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

Наверняка каждый, кто создавал бизнес-процесс в системе Terrasoft хотя бы раз сталкивался с задачей раздать разные права доступа пользователям на элемент бизнес-процесса. Вариант, когда пользователь 1 раздает вручную права доступа например, на документ по процессу пользователю 2 не совсем удобен (нужно всегда об этом помнить, делать лишние движения руками, мышью и т.д.). Волей-неволей задаешься задачей реализовать это один раз и больше не задумываться о подобных мелочах.
Предлагаю для решения задачи создать хранимую процедуру, которая и будет создавать нужные права доступа, а затем вызывать эту процедуру в любом месте скрипта бизнес-процесса.
Привожу текст хранимой процедуры в прикрепленном файле. Она предназначена для раздачи полных прав доступа одному пользователю и прав только на чтение другому (для документа).

После создания этой хранимой процедуры Вам необходимо создать ее обработку уже в Terrasoft CRM Administrator с передачей используемых в ней параметров. Это делается следующим образом:

function SetSpecialPermission(User1ID, User2ID) {
 if (IsEmptyValue(User1ID) || IsEmptyValue(User2ID)) {
  return;
 }
 var IsAdmin = Connector.CurrentUser.IsAdmin;
 IsAdmin = IsAdmin ? 1 : 0;
 var SQL = "exec tsp_SetSpecialPermissions :P1, :P2, :P3";
 var Param = System.CreateObject("TSObjectLibrary.Parameters");
    AddParameter(Param, pdtGUID, User1ID).Name = 'P1';
    AddParameter(Param, pdtGUID, User2ID).Name = 'P2';
    AddParameter(Param, pdtGUID, IsAdmin).Name = 'P3';
 Connector.DBEngine.ExecuteCustomSQL(SQL, Param);
}

Затем в нужном месте скрипта бизнес-процесса, например на событии OnAfterExecute задачи процессе, вызвать эту процедуру, указав ID пользователей.

 function Action1OnAfterExecute(ActionItem, IsComplete)

 var User1ID = '{251FB9AC-C17E-4DF7-A0CB-D591FDB97462}';
 var User2ID = '{B8ABDC0D-D36E-42C5-B1F6-2AD425F6CAD0}';
 SetSpecialPermission(User1ID, User2ID);
}

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

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

С уважением,
Мельникова Екатерина

Нравится

Поделиться

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

"Программа пытается получить доступ к адресам электронной почты, хранящимся в Outlook"
Нередко пользователи системы могут получать вот такое сообщение при попытке отправления e-mail из MS Outlook при включенном соединении с Terrasoft.
Если Вы обратитесь за информацией об этом сообщении на сайт Microsoft, то получите массу умной информации о том, почему это сообщение появляется, а в заключении примерно следующее:"...в общем случае запретить выдачу этого предупреждения невозможно".
Но службе технической поддержки все-таки удалось найти решение этой проблемы.
Для MS Outllok 2007:
- Открываем меню Сервис -> Центр управления безопасностью.
- В появившемся окне переходим на вкладку "Настройки" (слева).
- Проверьте наличие надстройки «Terrasoft CRM Add-In» и «Outlook advanced security».
- Для управления надстройками нажмите на кнопку «Перейти…».
- Убедитесь, что компоненты «Outlook advanced security» и «Terrasoft CRM Add-In» включены и путь к библиотеке актуален.
В противном случае удалите данные компоненты и создайте их заново
- Далее перейдите в меню «Сервис» -> «Advanced Security» -> «Action List…».
- Убедитесь, что данные действия постоянно разрешены (Allow Access). Если же это не так нажмите правой кнопкой мышки на модуле и предоставьте разрешение на выполнение (Actions -> Allow Access).
- Перезапустите MS Outlook.
Для MS Outlook 2003:
- Открываем меню Сервис -> Advanced Security -> Action List.Убедитесь, что данные действия постоянно разрешены (Allow Access). Если же это не так нажмите правой кнопкой мышки на модуле и предоставьте разрешение на выполнение (Actions -> Allow Access).
- Перезапустите MS Outlook.

Желаю удачи!

С уважением,
Мельникова Екатерина

Нравится

Поделиться

1 комментарий

а если после установки Advanced Security в меню Сервис Outlook не появилась эта закладка? в чём может быть проблема?

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