Добрый день!

Как Вы знаете, в карточке редактирования может быть представлено несколько представлений. Иногда требуется ограничить доступ к этому представлению некоторой группе пользователей. Этот функционал можно дополнительно реализовать средствами Terrasoft Administrator. Ниже приведу сам алгоритм, на примере карточки редактирования раздела "Продукты", в которой существует представление "Движение по складу", доступ к которому мы ограничим.

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

function IsUserInGroup(GroupID)
{
        var Dataset = Services.GetSingleItemByUSI('ds_AdminUnit');
        ApplyDatasetFilter(Dataset, 'UserContactID', Connector.CurrentUser.ContactID, true);
        Dataset.Open();
        var UserID = Dataset.ValAsGUID(IDFieldName);
        Dataset.Close();
        var Dataset = Services.GetSingleItemByUSI('ds_UserInGroup');
        ApplyDatasetFilter(Dataset, 'GroupID', GroupID, true);
        ApplyDatasetFilter(Dataset, 'UserID', UserID, true);
        Dataset.Open();
        var Is = (Dataset.RecordsCount > 0);
        Dataset.Close();
        return Is;
}

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

Далее необходимо отредактировать функцию function wnd_OfferingEditOnPrepare(Window). В ней добавим следующую проверку:

function wnd_OfferingEditOnPrepare(Window) {
        scr_BaseDBEdit.wnd_BaseDBEditOnPrepare(Window);
        Initialize(Window);            
        if(!Connector.CurrentUser.IsAdmin)
        {
                var UsrDataset = Services.GetSingleItemByUSI('ds_UserInGroup');                  
                var GroupName = 'Название';
                //где 'Название' - имя группы пользователей, для которых нужно ограничить доступ к представлению         
                ApplyDatasetFilter(UsrDataset, 'GroupName', GroupName, true);
                //тут следует не забыть создать фильтр сравнения в сервисе sq_UserInGroup (см. скриншот ниже)
                UsrDataset.Open();             
                var GroupID = UsrDataset.Values('GroupID');
                if(IsUserInGroup(GroupID))
                {
                        //скрываем само представление, установив свойству IsVisible значение false
                        pgOfferingAnalytic.IsVisible = false;  
                }
                UsrDataset.Close();
        }
       
}

Так же, перед тестированием результатов, следует создать фильтр сравнения в сервисе sq_UserInGroup:

2
3

После этого не забудьте сохранить изменения и перезапустить клиентское приложение Terrasoft.

Нравится

Поделиться

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

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

    var GroupDataset = Services.GetSingleItemByUSI('ds_UserInGroup');
    ApplyDatasetFilter(GroupDataset, 'UserID', UserID, true);
	     GroupDataset.Open();
	      GroupDataset.GotoNext();     //берем второе значение, так как первая по порядку группа 'Все пользователи'                 
         var GroupID = GroupDataset.ValAsStr('GroupID'); 
         GroupDataset.Close();

Большое спасибо за Ваш пример, очень пригодился!

В принципе есть базовая функция в scr_Access называется GetIsUserInGroup, делает примерно тоже самое только немного написана по другому

function GetIsUserInGroup(UserName, GroupID) {
	var sqGetIsUserInGroup = GetSingleItemByCode('sq_GetIsUserInGroup');
	SetParameterValue(sqGetIsUserInGroup.Parameters, 'GroupID', GroupID);
	SetParameterValue(sqGetIsUserInGroup.Parameters, 'UserName', UserName);
	var dsRes = sqGetIsUserInGroup.Open();
	try {
		return dsRes('IsExists') != 0;
	} finally {
		dsRes.Close();
	}
}
Показать все комментарии

Добрый день!
Скажите, пожалуйста, можно ли каким-то образом отражать во второй вкладке(или первой) окна редактирования карточки какую-либо закладку из этого же раздела. И при этом иметь возможность при этом удалять/добавлять записи на подобную закладку.

Нравится

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

Здравствуйте Дарья.
Приведем пример, добавим в карточку редактирования wnd_IncidentEdit закладку описание и отобразим в ней содержимое детали [Описание]

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

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

Добрый день!
Использовала скрипт в карточке на onprepare

var Dataset = dlData.Dataset;
if (TGroup.InitializeInvoicesFlag != true) {

TGroup.InvoicesWindow = wndTravellingGroup.Window;
SetAttribute(TGroup.InvoicesWindow, 'EditWindowUSI', 'wnd_TravellingGroupEdit');
SetAttribute(TGroup.InvoicesWindow, 'ParentItemFieldName', 'ContractID');
TGroup.InvoicesWindow.Prepare();
TGroup.InvoicesDataset =
TGroup.InvoicesWindow.ComponentsByName('dlData').Dataset;
TGroup.InitializeInvoicesFlag = true;

}

var ContractID = Dataset.ValAsGUID('ID');
if (ContractID == TGroup.InvoicesOldContractID) {
return;
} else {
TGroup.InvoicesOldContractID = ContractID;
}
SetAttribute(TGroup.InvoicesWindow, 'ParentItemID', ContractID);
RefreshDetailData(Dataset, 'ID', TGroup.InvoicesDataset, 'ContractID');
ApplyDatasetFilter(TGroup.InvoicesDataset, 'ContractID', Dataset.Values('ID'), true);

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

Дарья, опишите, пожалуйста, конкретно задачу, которую вы пытаетесь реализовать.

"Татаровская Дарья" написал:Скажите, пожалуйста, как можно сохранить родительскую запись перед созданием дочерней, если она не была сохранена?

Последовать рекомендациям Олега http://www.community.terrasoft.ua/forum/topic/799#comment-21195 не получилось ?

не получилось.
Сделали обходной путь - кнопка Сохранить в карточке контакта, перед тем как использовать кнопки на закладке.

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

"Татаровская Дарья" написал:не получилось.

Дарья, а можете показать как вы делали?
В вашем случае просто нужно при нажатии на любую из кнопок в реестре проверять в каком состоянии находится dataset карточки. Если состояние Insert, то автоматически делать сохранение. Но только в этом случае нужно сохранять где-то пометку о том, что карточка была автоматически сохранена. Потому что если пользователь нажмет кнопку Отмена -- нужно удалять созданную запись.

Дарья, вашу задачу можно реализовать следующим образом:
1) в скрипте к окну-гриду создаете свой обработчик нажатия кнопки btnAdd

function btnAddOnClick(Control) { 
	if (Self.Attributes('ParentItemFieldName') == 'ContractID')
	{
		var Dataset = Services.GetNewItemByUSI('ds_Contract'); 
		ApplyDatasetFilter(Dataset, 'ID', Self.Attributes('ParentItemID'), true); 
		Dataset.Open(); 
		if (Dataset.IsEmptyPage) 
	    	    SendNotify(Self, 'SAVE'); 
		else 
	    	    scr_BaseGridArea.btnAddOnClick(btnAdd); 
		Dataset.Close(); 
		return;
	}
 
	scr_BaseGridArea.btnAddOnClick(btnAdd); 
} 

Таким образом проверяете, сохранена ли родительская запись, и если нет - отправляете сообщение SAVE родительскому окну.

2) в том же скрипте создаете обработчик события окна OnNotify

function wnd_BaseGridAreaOnNotify(ScriptableService, Sender, Message, Data) {  
	if (Message == 'SAVED') 
	{ 
		scr_BaseGridArea.btnAddOnClick(btnAdd); 
		return; 
	} 
 
	scr_BaseGridArea.wnd_BaseGridAreaOnNotify(ScriptableService, Sender, Message, Data); 
};

Тут мы "ловим" сообщение SAVED, о том что родительская форма сохранила запись. И вызываем окно добавления записи.

3) и, наконец, в форме редактирования, в котором расположили это окно-грид, также создаете обработчик события OnNotify

function wnd_BaseDBEditOnNotify(ScriptableService, Sender, Message, Data) { 
	if (Message == 'SAVE') 
	{ 
	    if (SaveChangesWithCheck(Self, BaseDBEdit)) 
	    { 
	    	dlData.Dataset.Edit(); 
	        Sender.Notify(Self, 'SAVED', null); 
	    } 
	} 
} 

В нем мы получаем сообщение SAVE, пытаемся сохранить запись, и отправляем в ответ сообщение SAVED

Спасибо, Валерий, за совет

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

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

Предлагаю рассмотреть пример изменения Caption закладки "Адрес" раздела "Контрагенты" при наличии в ней записей на "Адрес*" (здесь можно придумать какие угодно варианты).
Итак, необходимо выполнить следующие действия:
1. Открываем сервис wnd_AccountsWorkspace, выделяем элемент деталей и переходим в обработчик события OnChangeActivePage.
2. В обработчике прописываем следующий код:

RefreshDetails();
 if (pcDetails.ActivePage.Name == pgAddressesDetail.Name) {   // вместо pgAddressesDetail нужно вставить название //Вашей закладки
  if (!IsDatasetEmpty(wndAddressesDetail.Window.ComponentsByName('dlData').Dataset)) { // вместо wndAddressesDetail нужно вставить название окна //закладки
   pgAddressesDetail.Caption = 'Адреса';
  } else {
   pgAddressesDetail.Caption = 'Адреса*'
  }
 }

Вышеприведенный код реализует смену Caption закладки при смене активной закладки в менеджере деталей.
3. Затем откройте закладку "Невизуальные", выделите датасет dlAccounts и перейдите в обработчик события OnDatasetAfterPositionChange.
4. Вставьте в обработчик события следующий код:
if (Dataset.Attributes('IsNew') != true) {
  RefreshDetails();
 }
 Dataset.Attributes('IsNew') = false;
 var AccountID = Dataset.Values('ID');
 var AddressDataset = Services.GetNewItemByUSI('ds_AccountAddress'); // вместо //ds_AccountsAddress нужно указать датасет соответсвующей //закладки
 ApplyDatasetFilter(AddressDataset, 'AccountID', AccountID, true);
 AddressDataset.Open();
 var AddressDatasetIsEmpty = IsDatasetEmpty(AddressDataset);
 AddressDataset.Close();
 if (!AddressDatasetIsEmpty) {
  pgAddressesDetail.Caption = 'Адреса';
 } else {
  pgAddressesDetail.Caption = 'Адреса*'
 }

Вышеприведенный код реализует смену Caption закладки при переходе по записям в основном реестре.
5. Сохраните все внесенные изменения. Перезапустите рабочее приложение Terrasoft CRM и протестируйте работоспособность системы.

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

Желаю удачи!

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

Нравится

Поделиться

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

А зачем код в OnChangeActivePage?

Код в OnChangeActivePage реализует смену Caption закладки при смене активной закладки в менеджере деталей. Конкретный пример: у пользователя активна одна запись в реестре и какая-то закладка в менеджере деталей. В этот момент другой пользователь может добавить записи на закладку, которая сейчас у первого не активна. Таким образом, при переходе на эту закладку первым пользователем - Caption меняется.

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

"Underscore a.k.a. _" написал:Так основной смысл был знать наличие записей до перехода на закладку

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

"Ключник Алексей" написал:До перехода и будем знать

Я так понимаю, OnChangeActivePage вызывается непосредственно в момент перехода.

В данном случае не важно когда. Если мы переходим с детали Средства Связи на деталь Группы, то все равно увидим актуальную информацию по детали Адреса относительно наличия данных в ней.

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

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

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

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

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

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

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

--обновляет средсва связей и адреса контактов по их контрагентам.
--Детали адреса и средства связей контактов наполняются по их контрагентам, при этом старые записи остаются
-- если старые записи не нужны убрать/закомментировать строчку помеченную /*!!!*/
declare @NeedOldDetailRecords int
set @NeedOldDetailRecords = 0
set @NeedOldDetailRecords = 1   /*!!!*/


print 'Update tbl_Contact'
update t1
set     t1.Communication1=t2.Communication1,
    t1.Communication2=t2.Communication2,
    t1.Communication3=t2.Communication3,
    t1.Communication4=t2.Communication4,
    t1.Communication1TypeID=t2.Communication1TypeID,
    t1.Communication2TypeID=t2.Communication2TypeID,
    t1.Communication3TypeID=t2.Communication3TypeID,
    t1.Communication4TypeID=t2.Communication4TypeID,
    t1.TerritoryID    =t2.TerritoryID,
    t1.AddressTypeID =t2.AddressTypeID,
    t1.CountryID =t2.CountryID,
    t1.StateID =t2.StateID,
    t1.CityID =t2.CityID,
    t1.ZIP =t2.ZIP,
    t1.Address =t2.Address
from tbl_Contact t1, tbl_Account t2
where t1.AccountID is not null
and t2.id=t1.AccountID

if @NeedOldDetailRecords = 1
begin
    print 'update tbl_ContactCommunication'
    update tbl_ContactCommunication
    set position = null
    where exists(select * from tbl_Contact t2 where tbl_ContactCommunication.ContactID=t2.ID and t2.AccountID is not null)
   
    print 'update tbl_ContactAddress'
    update tbl_ContactAddress
    set IsPrimary = null
    where exists(select * from tbl_Contact t2 where tbl_ContactAddress.ContactID=t2.ID and t2.AccountID is not null)
end
if @NeedOldDetailRecords = 0
begin
    print 'delete from tbl_ContactAddress'
    delete tbl_ContactAddress
    from tbl_ContactAddress t1
    where exists(select * from tbl_Contact t2 where t1.ContactID=t2.ID and t2.AccountID is not null)
   
    print 'delete from tbl_ContactCommunication'
    delete tbl_ContactCommunication
    from tbl_ContactCommunication t1
    where exists(select * from tbl_Contact t2 where t1.ContactID=t2.ID and t2.AccountID is not null)
end

print 'Insert into tbl_ContactAddress'
insert into tbl_ContactAddress(id,ContactID,AddressTypeid,Address,CityID,StateID, Zip, CountryID, IsPrimary, TerritoryID)
select newid(), t1.id, t3.AddressTypeid, t3.Address, t3.CityID, t3.StateID, t3.Zip, t3.CountryID, t3.IsPrimary, t3.TerritoryID
from tbl_Contact t1, tbl_Account t2, tbl_AccountAddress t3
where t1.accountid is not null
and t1.accountid=t2.id
and t2.id=t3.accountid

print 'Insert into tbl_ContactCommunication'
insert into tbl_ContactCommunication(id,ContactID,Number,CommunicationTypeID,Position,Digits)
select newid(), t1.id, t3.Number, t3.CommunicationTypeID, t3.Position, t3.Digits
from tbl_Contact t1, tbl_Account t2, tbl_AccountCommunication t3
where t1.accountid is not null
and t1.accountid=t2.id
and t2.id=t3.accountid

С уважением,
Коваленко Ирина

Нравится

Поделиться

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

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

   function InsertFilesLink(FileID) {             var InsertQuery = FilesDetailGridArea.InsertQueryLink;             var ColumnsValues = InsertQuery.ColumnsValues;     ColumnsValues.ItemsByName('FileID').Value = FileID;     ColumnsValues.ItemsByName(BaseGridArea.ItemIDFieldName).Value =                         FilesDetailGridArea.ParentItemID;     var ID = Connector.GenGUID();     ColumnsValues.ItemsByName('ID').Value = ID;     InsertQuery.Execute();     if (BaseGridArea.ItemIDFieldName == 'AccountID') { //определяем, является ли деталь, деталью в контрагентах             if (!FilesDetailGridArea.ContactInsertQueryLink) {                         FilesDetailGridArea.ContactInsertQueryLink =                                                             Services.GetNewItemByUSI('iq_fileInContact');             }             var InsertQueryContact = FilesDetailGridArea.ContactInsertQueryLink;             var ColumnsValuesContact = InsertQueryContact.ColumnsValues;             ColumnsValuesContact .ItemsByName('FileID').Value = FileID;             var AccountID = FilesDetailGridArea.ParentItemID;             ColumnsValuesContact .ItemsByName('ContactID').Value = GetPrimaryContactByAccountID(AccountID);               var ID = Connector.GenGUID();             ColumnsValuesContact .ItemsByName('ID').Value = ID;             InsertQueryContact.Execute();     } }  

Для того, чтобы функция выполнялась корректно Вам необходимо создать функцию GetPrimaryContactByAccountID(AccountID), которая по заданному AccountID определит Оснойной контакт этого контрагента и отладить предоставленный нами код в отладчике скриптов. Аналогично может быть реализована подобная функциональность и в других разделах. С уважением, Коваленко Ирина

Нравится

Поделиться

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

Полезный функционал!!!

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

Иногда в процессе работы с системой возникает необходимость отобразить все записи в основном реестре, у которых есть прикрепленные файлы на закладке "Файлы".
Для решения этой задачи предлагаю один вариант решения, рассмотренный на примере раздела "Контрагенты". Для остальных разделов можно сделать по аналогии.
Хочу отметить, что самое сложное в этой задаче - это создать правильный фильтр в запросе. Но это не проблема... Попытаюсь описать как можно более доходчиво, как это можно сделать:

1. Добавляем колонку подзапроса в sq_Account (называем HasFile).
2. В FROM указываем связь с таблицей tbl_FileInAccount (псевдоним FileInAccount1)
3. Здесь же добавляем еще одну связь с таблицей tbl_Files (LEFT OUTER JOIN).
4. В Select создаем поле ID (делаем выборку из tbl_FileInAccount).
5. В условии Where создаем фильтр сравнения AccountID (tbl_Account.ID)= FileInAccount1.AcountID
6. Здесь же в Where создаем фильтр сравнения (Compare Filter).
7. В этом фильтре в FROM указываем связь с таблицей tbl_FileInAccount (псевдоним FileInAccount2)
8. В этом же фильтре создаем поле ID (в Select). Делаем выборку из FileInAccount2.
9. В Where создаем фильтр сравнения AccountID (FileInAccount2.AccountID = tbl_Account.ID).
Итак, сохраняем изменения и можно считать, что самое сложное сделано!
Добавляем в датасет ds_Account булевское поле, в поле "Колонка" выбираем наше поле HasFile из запроса, даем ему заголовок "Есть файл". Добавляем в грид колонку.
Перезапускаем рабочее приложение Terrasoft CRM и проверяем созданный функционал.

Желаю удачи!

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

Нравится

Поделиться

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

Еще есть один вариант настройки пользовательского фильтра на наличие файлов у записи (на примере задач).

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