Всем привет!
Все мы знаем, что, если при удалении записи на нее есть ссылки из других таблиц, то система выдает список связей для того, чтобы пользователь попробовал разобраться с этим, где нужно удалил эти связи или вообще отказался от затеи удалять ту самую запись. Но, на мой взгляд, в 90% случаев пользователь уверен, что эту запись точно нужно удалить и разбираться со связями он 100% не хочет. Так почему бы не дать ему возможность просто удалить эту запись, предварительно отвязав ее от остальных объектов автоматически?

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

1. Создаем хранимую процедуру под sa или под пользователем с правом sysadmin, которая будет делать отвязку записи и ее удаление:

CREATE procedure tsp_UnbindAndDeleteRecord(@RecordID nvarchar(max), @ParentTableName nvarchar(max)) WITH exec AS owner AS
begin
        declare @ColumnName nvarchar(max)
        declare @TableName nvarchar(max)
        declare @TempSQL nvarchar(max)
        declare @RecordValue nvarchar(max)

        declare local_cursor cursor LOCAL FOR
        SELECT DISTINCT
                CONSTRAINT_COLUMN_USAGE.COLUMN_NAME AS COLUMN_NAME,
                CONSTRAINT_COLUMN_USAGE.TABLE_NAME AS TABLE_NAME
        FROM ((INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS CONSTRAINT_COLUMN_USAGE
        INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TABLE_CONSTRAINTS
                ON CONSTRAINT_COLUMN_USAGE.CONSTRAINT_NAME = TABLE_CONSTRAINTS.CONSTRAINT_NAME)
        INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS REFERENTIAL_CONSTRAINTS
                ON CONSTRAINT_COLUMN_USAGE.CONSTRAINT_NAME = REFERENTIAL_CONSTRAINTS.CONSTRAINT_NAME)
        INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS PARENT_TABLES
                ON REFERENTIAL_CONSTRAINTS.UNIQUE_CONSTRAINT_NAME = PARENT_TABLES.CONSTRAINT_NAME
        WHERE TABLE_CONSTRAINTS.CONSTRAINT_TYPE = 'FOREIGN KEY' AND
                PARENT_TABLES.TABLE_NAME = @ParentTableName

        IF (isnull(cast(@RecordID AS nvarchar(max)), '') = '')
                RETURN

        open local_cursor
        fetch next FROM local_cursor INTO @ColumnName, @TableName

        while @@fetch_Status = 0
        begin
                SET @TempSQL = 'UPDATE [' + @TableName + '] SET [' + @ColumnName + '] = null' + ' WHERE ['+ @ColumnName + '] = ''' + @RecordID + ''''
                exec sp_executesql @TempSQL
                fetch next FROM local_cursor INTO @ColumnName, @TableName
        end
        close local_cursor
        deallocate local_cursor

        SET @TempSQL = 'delete from [' + @ParentTableName + '] WHERE [ID] = ''' + @RecordID + ''''
        exec sp_executesql @TempSQL
end

--grant execute on tsp_UnbindAndDeleteRecord to public

Как вы может быть заметили, процедура создана с "with exec as owner". Добавил это я на всякий случай, если вдруг вы не дадите гранты на использование этой процедуры пользователям.

2. Ищем сервис окна wnd_DeleteRecordsWizard, которое появляется, когда выводится список связанных объектов при удалении записей. Добавляем во фрейм с кнопками свою кнопку btnDeleteForce и даем ей "Заголовок" = "Удалить принудительно".

3. На OnClick этой кнопки "вешаем" наш код, в котором запускаем нашу процедуру из пункта 1, передавая в нее в качестве параметров ID удаляемых записей (у нас их ведь может быть сколько угодно) и имя таблицы, из которой производится удаление.

function btnDeleteForceOnClick(Control) {
        var Dataset = dlData.Dataset;
        if (Dataset.IsEmptyPage) {
                return;
        }
        if (ShowConfirmationDialog("Вы уверены?") != wmrYes) {
                return;
        }
        var TableName = Self.Attributes('TableName');
        if (IsEmptyValue(TableName)) {
                return;
        }
        var RecordIDs = Self.Attributes('RecordIDs');
        if (!RecordIDs) {
                return;
        }
        var Count = RecordIDs.length;
        var RecordID;
        try {
                System.BeginProcessing();
                for (var i = 0; i Count; i++) {
                        System.ProcessMessages();      
                        RecordID = RecordIDs[i];
                        var sql = "exec tsp_UnbindAndDeleteRecord '" +
                                RecordID + "', '" + TableName + "'";
                        Connector.DBEngine.ExecuteCustomSQL(sql, System.EmptyValue);
                }
        } finally {
                System.EndProcessing();
        }
        Self.Close();
}

4. Все-таки позволять всем пользователям принудительно удалять запись - дело рискованное. Поэтому предлагаю позволять это делать ответственному человеку, а именно пользователю с правами администратора системы. Для этого в OnPrepare окна wnd_DeleteRecordsWizard добавляем код показа нашей кнопки:

btnDeleteForce.IsVisible = Connector.CurrentUser.IsAdmin;

5. Сохраняем сервисы и проверяем.

Если же вы, все-таки, решите позволять такое удаление любому пользователю, то, как понимаете, пункт 4 делать не нужно.

Примечание: Проверялся этот функционал на MS SQL 2008 (для Oracle он вообще не подходит, как Вы можете судить по синтаксису хранимой процедуры), но, думаю, он будет работать и на MS SQL 2005.

Нравится

Поделиться

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

Спасибо S.Kalishenko. Полезный функционал. Только я так понял доработка для версии террасофт начиная с 3.3.2 так как в версии 3.3.1 еще нет окна wnd_DeleteRecordsWizard. Я думаю как вариант сделать в окне wnd_BaseGridArea в amDelete еще один пунк "Удалить принудительно" и поправить код следующим образом

var Dataset = dlData.Dataset;
        if (Dataset.IsEmptyPage) {
                return;
        }
        if (ShowConfirmationDialog("Вы уверены?") != wmrYes) {
                return;
        }
        var TableName = GetTableFromDataset(Dataset);
        if (IsEmptyValue(TableName)) {
                return;
        }
        var RecordIDs = GetGridSelectedIDsArray();
        if (!RecordIDs) {
                return;
        }
        var Count = RecordIDs.length;
        var RecordID;
        try {
                System.BeginProcessing();
                for (var i = 0; i < Count; i++) {
                        System.ProcessMessages();      
                        RecordID = RecordIDs[i];
                        var sql = "exec tsp_UnbindAndDeleteRecord '" +
                                RecordID + "', '" + TableName.SQLName + "'";
                        Connector.DBEngine.ExecuteCustomSQL(sql, System.EmptyValue);
                }
        } finally {
                System.EndProcessing();
        }

а и еще вопрос а возможно сделать деталь в которой бы отображалось в каких таблицах данная запись имеет связанные записи?

Клёвая штука! Надо использовать обязательно!

"Мещеринов Иван Александрович" написал:Только я так понял доработка для версии террасофт начиная с 3.3.2 так как в версии 3.3.1 еще нет окна wnd_DeleteRecordsWizard.

Да, в 3.3.1 можно реализовать функционал как Вы описали.

"Мещеринов Иван Александрович" написал:а и еще вопрос а возможно сделать деталь в которой бы отображалось в каких таблицах данная запись имеет связанные записи?

Можно, только код я не готов Вам предоставить. Для такой детали можно воспользоваться реализацией получения датасета в wnd_DeleteRecordsWizard из 3.3.2.

Здравствуйте, сообщество.

Сегодня сделал аналогичный функционал для БД Oracle.

Изменяем метод btnDeleteForceOnClick(Control) до следующего вида:

function btnDeleteForceOnClick(Control) {
	ForceDeleteRecord(dlData.Dataset, 
		Self.Attributes('TableName'), Self.Attributes('RecordIDs'));
	Self.Close();
}

Добавляем метод ForceDeleteRecord(Dataset, TableName, RecordIDs) в тот же модуль:

function ForceDeleteRecord(Dataset, TableName, RecordIDs) {
	if (!Dataset || IsEmptyValue(TableName)) {
		return;
	}
	if (Dataset.IsEmptyPage) {
		return;
	}
	if (ShowConfirmationDialog("Вы уверены?") != wmrYes) {
		return; 
	}
	if (!RecordIDs) {
		return;
	}
	var Count = RecordIDs.length;
	var RecordID; 
	try {
		System.BeginProcessing();
		for (var i = 0; i < Count; i++) {
			System.ProcessMessages();       
			RecordID = RecordIDs[i];
			var sql = '';
			switch (Connector.DBExecutor.DBExecutorTypeCode) {
				case 'MSSQL':
					var sql = "exec tsp_UnbindAndDeleteRecord '" + 
						RecordID + "', '" + TableName + "'";
					break;
				case 'Oracle':
					var sql = "begin \"tsp_UnbindAndDeleteRecord\""('"" + 
Показать все комментарии

При удалении Задачи - задача является элементом процесса
При удалении Процесса - Связанные записи могут сосуществоать в следующих таблицах tbl_WorkflowItem
При удалении Элемента - Связанные записи могут существовать в следующих таблицах tbl_Task
ВСЁ круг замкнулся. ПОМОГИТЕ!

Нравится

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

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

Через клиент Terrasoft у вас не получиться удалить ни одну из перечисленных вами сущностей.
Вам придется либо менять функционал в администраторе Terrasoft, либо написать в администарторе скрипт, который будет удалять нужные вам записи, либо использовать sql-запросы для удаления нужных записей из БД.

Например, если задача привязана к бизнес-процессу, то из клиента Terrasoft вы не сможете удалить ее, но вы сможете ее удалить, используя простой запрос на базе данных:

delete from tbl_task where id = '{id задачи}'

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

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

Как Вы заметили, удаление бизнес-процессов не предусмотрено в системе Террасофт. Они задуманы быть «вечными» и завершаться после перехода всех элементов в конечную стадию.

Есть два варианта удаления процессов:

Первый вариант

При возникновении «подвисших» процессов есть возможность вручную перевести все незавершенные элементы в конечное состояние (таким образом подтолкнем на механизм завершения процесса). При этом пропадут записи из раздела «Процессы», но останутся связи с объектами системы (например, с задачами). Если Ваша задача – почистить раздел «Процессы» от «подвисших», то этот вариант именно то, что нужно.

Открыть запрос CheckWorkflow.sql (архив scr.rar в прикрепленных файлах)

В первой строчке указать название БД.
В строчках

select @Name = 'wd_Process_name_here'
select @EndState = '{End_State_ID_here}'

указать нужное имя процесса и идентификатор конечного состояния элементов процесса. Его можно узнать в сервисе enm_WorkflowItemState (Workflow\General\Items\enm_WorkflowItemState):
sce1

Проверить, какие элементы выбираются.

Если все в порядке и в результатах запроса именно зависшие, то открыть запрос DeleteWorkflow.sql (архив scr.rar в прикрепленных файлах), произвести такие же изменения строчек и выполнить запрос.
Перезапускать Террасофт не нужно, просто обновить реестр.

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

1. Запустить SQL Management Studio
2. Открыть запрос DeleteWorkflowAll.sql, прикрепленный к письму.
3. Указать во второй строчке название процесса, который следует удалить.
4. Выполнить скрипт. У всех зависимых элементов должны удалиться связи с процессом.

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

Также предлагаю вам SQL запросы для удаления записей в соответвующих сущностях.

delete from frb."tbl_Task"
where "WorkflowItemID" in (select "ID" 
from frb."tbl_WorkflowItem"
where "WorkflowID" in
(select "ID"
from frb."tbl_Workflow"
where "CreatedByID" in
(select "ID" from frb."tbl_Contact"
where "Name" like '%ФИО пользователя, для которого нужно завершить БП%'
)and "Name" = 'Название БП')
)
 
delete from frb."tbl_Document"
where "WorkflowItemID" in (select "ID" 
from frb."tbl_WorkflowItem"
where "WorkflowID" in
(select "ID"
from frb."tbl_Workflow"
where "CreatedByID" in
(select "ID" from frb."tbl_Contact"
where "Name" like '%ФИО пользователя, для которого нужно завершить БП%'
)and "Name" = 'Название БП')
)
 
delete from frb."tbl_Incident"
where "WorkflowItemID" in (select "ID" 
from frb."tbl_WorkflowItem"
where "WorkflowID" in
(select "ID"
from frb."tbl_Workflow"
where "CreatedByID" in
(select "ID" from frb."tbl_Contact"
where "Name" like '%ФИО пользователя, для которого нужно завершить БП%'
)and "Name" = 'Название БП')
)
 
delete from frb."tbl_Invoice"
where "WorkflowItemID" in (select "ID" 
from frb."tbl_WorkflowItem"
where "WorkflowID" in
(select "ID"
from frb."tbl_Workflow"
where "CreatedByID" in
(select "ID" from frb."tbl_Contact"
where "Name" like '%ФИО пользователя, для которого нужно завершить БП%'
)and "Name" = 'Название БП')
)
 
delete from frb."tbl_Opportunity"
where "WorkflowItemID" in (select "ID" 
from frb."tbl_WorkflowItem"
where "WorkflowID" in
(select "ID"
from frb."tbl_Workflow"
where "CreatedByID" in
(select "ID" from frb."tbl_Contact"
where "Name" like '%ФИО пользователя, для которого нужно завершить БП%'
)and "Name" = 'Название БП')
)
 
delete from frb."tbl_AccountIndustry"
where "WorkflowItemID" in (select "ID" 
from frb."tbl_WorkflowItem"
where "WorkflowID" in
(select "ID"
from frb."tbl_Workflow"
where "CreatedByID" in
(select "ID" from frb."tbl_Contact"
where "Name" like '%ФИО пользователя, для которого нужно завершить БП%'
)and "Name" = 'Название БП')
)

P.S Перед изменениями описанными выше нужно обязательно сделать резервную копию базы данных. В случае необходимости обратитесь за помощью к системному администратору или сотруднику, решающему вопросы с базами данных.

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

Добрый день помогите пожалуйста, возникла проблемка при удалении контрагента. Все существующие связи контрагента были удалены. При удалении самого контрагента возникает такая вот ошибка: "Ошибка удаления записи. Недопустимое имя объекта "dbo.Name". Я так подозреваю, что это ошибка в тригере, вот только не могу найти где это.

Нравится

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

Проверьте текст триггера [dbo].[tr_tbl_Account_ID]. Скорее всего, в нём.

Да именно там мы искали. Вроде как нашли текст ошибки, а вот что вместо неё должно быть не можем понять. Текст тригера прикреплён с подчеркнутыми предполагаемыми проблемными местами. Помогите разобраться!

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

,(select [Name].[tbl_Country]
from [dbo].[Name] as [Name]
where [Name].[ID] = [D].[CountryID])
as [CountryName]

на такой:

,(select [tbl_Country].[Name]
from [dbo].[tbl_Country] as [tbl_Country]
where [tbl_Country].[ID] = [D].[CountryID])
as [CountryName]

Большое спасибо Олег, всё получилось.

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

При удалении контрагента выдается сообщение, что не удалена запись так как нет возможности - смотрите системный журнал, а в журнале пишет следующее "Ошибка удаления записи. Оригинальное сообщение: Недопустимое имя объекта "dbo.Communication2TypeName"". Вся связанная информация по данному контрагенту была откреплена во всех таблицах. Помогите что не так?

Нравится

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

Добрый день, Сергей.
Странная ошибка. Выполняли ли с этим разделом какие-либо изменения в настройках? Например, изменяли состав полей или внешних ключей?
Попробуйте с помощью Profiler получить запрос "DELETE FROM ...", который выполняет попытку удаления, выполните отдельно в Management Studio, если не получится - текст ошибки будет более понятный. Если ничего не помогает - советую обратиться в службу поддержки.

Сергей, проверьте триггер tr_tbl_Account_ID в той части, где происходит запись значения Communication2TypeName в таблицу логов tbl_AccountLog. Мне встречались случаи, когда вместо корректного запроса

(select [tbl_CommunicationType].[Name]
      from [dbo].[tbl_CommunicationType] as [tbl_CommunicationType]
      where  [tbl_CommunicationType].[ID] = [D].[Communication2TypeID])
    as [Communication2TypeName]

триггер содержал что-то типа

(select [Communication2TypeName].[ID]
      from [dbo].[Communication2TypeName] as [tbl_CommunicationType]
      ...

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

Уважаемый Олег, выполнили все как Вы написали, ошибка пропала но появилась новая :
Ошибка удаления записи. Оригинальное сообщение: Ошибка при преобразовании строки символов в тип uniqueidentifier

Сергей, Вы заменили только ту часть, которая касалась Communication2TypeName? Посмотрите на аналогичные участки, которые соответствуют Communication1Type, Communication3Type, Communication4Type. Для Communication2Type должно быть точно так же, как и для них, кроме соответствующего индекса.

Если отличий нет, воспользуйтесь советом Дмитрия: перед удалением включите Profiler и посмотрите, какой запрос выполняется при удалении, а потом попробуйте его выполнить в Management Studio.

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

Олег большое спасибо, всё получилось, просто допустили орфографическую ошибку при записи в тригер:sad:

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

На этапе внедрения раздела "библиотека" столкнулся с такой проблемой. Пользователи внося новые записи и распределяя их по группам постоянно удаляют их физически из раздела.
Т.е человек создал в группе1 запись1, отнес запись1 еще к группе2 и группе3. Потом, через некоторое время, решил, что в группе3 ему запись1 не нужна и нажал кнопку удалить в реестре записей группы, тем самым удалив физически запись1 из всех групп и бд. Хотелось бы этого избежать, понял, что нравоучениями этот вопрос не решить и нужно идти на встречу пользователю и делать как ему удобно.
Написав в тех поддержку я предложил на кнопку удалить, которая в реестре записей, назначить действие удалить из группы и добавить в контекстное меню кнопки удалить еще 2 действия "удалить из группы", "удалить запись" , но мне ответили, что это не рационально. Может кто сталкивался с такой проблемой и может подсказать как реализовать?

Нравится

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

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

IMHO административно (терпеливым обучением пользователей либо радикально удаленная запись = -$5) решить лучше, так как аналогичный механизм во всех разделах Terrasoft:wink:
Либо модифицировать всю систему насквозь. Как вариант реализовать при удалении записи проверку входит ли она в более чем одну группу, отличную от корневой - и окошко предупреждения - "А вот уверены ли вы?". Но это путь, повторюсь, тяжелее.

 

"Александр Кудряшов" написал:MHO административно (терпеливым обучением пользователей либо радикально удаленная запись = -$5) решить лучше, так как аналогичный механизм во всех разделах Terrasoft:wink:
Либо модифицировать всю систему насквозь. Как вариант реализовать при удалении записи проверку входит ли она в более чем одну группу, отличную от корневой - и окошко предупреждения - "А вот уверены ли вы?". Но это путь, повторюсь, тяжелее.

Александр, обучение дало свой результат. тут скорее человеческий фактор. Насчет штрафов - не пройдет такая идея, ибо моя роль в проекте реализовать его на 100 процентов удобным для пользователей, так что нужно что-то решать)

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

Мы обязательно сообщим Вам, как только данная возможность будет реализована.

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

День добрый.. Запустил случайно бизнес процесс, а кнопки удалить нет. Скажите, каким образом можно его удалить????

Нравится

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

Стандартной функции удаления БП нет, тольку "руками" из базы. Но будьте аккуратны... :)

радикальный метод - запросами напрямую из базы:wink:
БП - сущность в CRM, которая задумана как идеологически "вечная" и удалению не подлежащая (по-умолчанию)

Не, ну из базы данных запросом то это, конечно, можно, мне собственнои пришлось сделать. Но должен же быть более не топорный способ удалить из самой ЦРМ или остановить уж...
Вообще такая возможность не предусмотрена и не будет реализована????

В системе не предусмотрено удаление бизнес-процессов (WorkflowDiagram), если они уже выполнялись и существуют порожденные элементы (например, задачи).
Это сделано специально для сохранения целостности базы данных.
Мы не советуем удалять существующие бизнес процессы.
 
Но, если все-таки нужно это сделать, т.е. удалить бизнес-процесс, нужно сначала отвязать от него все порожденные элементы.
Для этих целей мы написали скрипт (см. во вложении). Сервисы нужно загрузить с помощью TSAdmin.
 
В начале скрипта scr_RemoveWorkflow (Common\Tools\Other\RemoveWorkflow\RemoveWorkflow) нужно изменить значение переменной ServiceCode на код сервиса удаляемого бизнес-процесса.
Запустить скрипт (F9). Результаты работы выводятся в лог.
 
После этого WorkflowDiagram можно удалять.

создал процесс wd_test... Теперь хочу его с помощью вашего скрипта удалить... Но скрипт ругается на sq_Services, ибо такового у меня нет :( Подскажите где взять?

var ServiceCode = 'wd_test';
Log.Write(1, 'Start clear workflow: ' + ServiceCode);
var sq = Services.GetNewItemByUSI('sq_Services');

'sq_Service'

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

Распространённая ситуация: надо очистить справочник от лишних записей. Когда это записи вроде «test», никаких проблем возникнуть не должно. Но если название не такое «кричащее», появляются сомнения: а нет ли привязки к ID этой записи где-то в скрипте? Добро, если все ID аккуратно сложены в scr_Consts (где им и полагается быть), можно посмотреть туда и выяснить. Но бывает, что они валяются где попало. Даже заглянуть в scr_Consts – дело не быстрое, особенно, когда записей много. А уж искать по всем скриптам – огромная трата времени. К тому же, записи из справочников может удалить и пользователь, а он, порой, и не догадывается, что к ID удаляемой записи привязана какая-то логика. Как же быть? Была мысль сделать служебное поле «запрет удаления записи», но это только оттянет решение проблемы: рано или поздно встанет вопрос, ставить туда галочку, или нет. Да и добавлять такое поле в каждую таблицу каждого справочника, да логику обработки запретов, да приучиться пользоваться новым полем – всё это сложно и громоздко.

Предлагаю другой путь: при удалении записи из таблицы справочника действительно автоматически проверять, не указан ли её ID где-то в скриптах. И если указан – сообщать об этом, запрещая удаление. Положительные стороны такого подхода: быстрая реализация и надёжность. Отрицательная сторона – медленное удаление из справочника. Всё-таки, проверить ВСЕ скрипты на вхождение строки ID не быстро. В среднем выходит около трёх секунд. Однако, практика показала, что это не слишком высокая высокая плата за надёжность.

Вот, что следует сделать:

1. Эту функцию вставить в scr_Utils:

function FindTextInService(FindText){
//@АБ(all)
/*
Функция для поиска вхождения строки аргумента в тексте всех скриптов
Возвращает название скрипта, если строка найдена, и '', если такой строки нет
*/

        var ServiceCount = Services.InformationsCount;
        for (var i = 0; i ServiceCount; i++) {
            var Info = Services.Informations(i);
            var ServiceTypeCode = Info.ServiceTypeCode;
            var USI = new String(Info.USI);
            var CaptionUSI = ExtractUSICodeEx(Info.USI);
            if (ServiceTypeCode == 'Script'){
                        try {
                            if (USI.indexOf('Call Centre') >= 0){
                            //для  call-центра нет лицензии
                                    continue;
                            }
                        var Service = Services.GetSingleItemByUSI(CaptionUSI);
                        var ServiceText = new String(Service.Text);
                                if (ServiceText.indexOf(FindText) >= 0){
                                        //Если в тексте скрипта найдена строка поиска
                                        //Log.Write(1, 'Строка '+FindText+' найдена в скрипте '+CaptionUSI);
                                return CaptionUSI;
                                }
                   }
                   catch (e){
                   //Вдруг ошибки лицензий
                   }
                }
    }
        return '';
}

2. В scr_WindowUtils вставить в функцию DeleteDataGridRecords(DataGrid)

после строк:

if (KeyDataFieldNotExist) {
                var Message = FormatStr(KeyDataFieldNotAssignedInDataset, Dataset.USI);
                throw (Message);
        }

вставить:

        //@АБ Вставка проверки на возможность удаления из справочников часть 1
        //(здесь определяется, что это - справочник.
        //Критерии: либо сервис в папке 'Dictionaries',
        //либо относится к группе таблиц 'TG_DICTIONARY')
        var DatasetUSI = new String(Dataset.USI);
        var IsDictionaryDataset = ((GetParentTableGroup(Dataset) == 'TG_DICTIONARY')||
                (DatasetUSI.indexOf('Dictionaries') > 0));
        //


и перед

} catch (e) {


вместо

Dataset.Delete();


вставить:

//@АБ Вставка проверки на возможность удаления из справочников
                        if (IsDictionaryDataset){
                        //Если удаляем из справочника
                                System.BeginProcessing(); //Начало показа
                                var FindScript = FindTextInService(ID);
                                System.EndProcessing(); //Конец показа
                                if (FindScript != ''){
                                //Если в скриптах используется такой ID, запрещаем удаление
                                    ShowWarningDialog('Удаление невозможно:\nЗапись c ID = \''+
                                                ID+'\'\n используется в скрипте '+FindScript);
                                        DeleteNoPossibility = DeleteNoPossibility + 1;
                                        continue;
                                } else {
                                //Запись не найдена - удаляем
                        Dataset.Delete();
                                }
                        } else {
                                Dataset.Delete();
                        }

Предложенная проверка не работает для идентификаторов, "зашитых" в параметры SelectQuery. Если есть опасность удалить такую запись, то стоит написать функцию, аналогичную описанной выше FindTextInService(FindText), но для поиска в параметрах всех запросов. Принцип тот же.

Нравится

Поделиться

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

"Будак Анатолий Васильевич" написал:catch (e){
//Вдруг ошибки лицензий
}

Предлагаю все же не делать проверку на ошибки лицензий через перехват исключений. Это может привести к неприятным последствиям в случае возникновения других типов ошибок. Лучше воспользоваться System.GetHasLicense.

Согласен насчет актуальности данной проблемы. Но по моему опыту часто возникает необходимость в такой проверке тогда, когда производится очистка базы. Мое предложение - хранить все константы в таблице констант. Один раз при запуске системы объявлять эти константы непосредственно в скрипте scr_Consts так, например:

function InitVars() {
	var ConstsDataset = Services.GetNewItemByUSI('ds_Consts');
	ConstsDataset.FetchRecordsCount = -1;
	ConstsDataset.Open();
	var Code, ID;
	while (!ConstsDataset.IsEOF) {
		ID = ConstsDataset.Values('ID');
		Code = ConstsDataset.Values('Code');		
		eval(FormatStr('%1 = \'%2\';', Code, ID));
		ConstsDataset.GotoNext();
	};
	ConstsDataset.Close();
};

А в самих таблицах создавать триггерок с маленьким запросом EXISTS из этой самой таблицы.
Преимущества такого подхода:
1. Целостность на уровне СУБД;
2. Часто в хранимых ф-ях и процедурах, триггерах, вьюхах так же используются константы. Можно будет эти значения не задавать, а делать запрос из этой таблицы;
3. Удобный просмотр (с сортировками, группировками), а также добавление новых констант (без возможности продублировать айдишник, как это часто бывает в проектах);
4. Скорость удаления записей практически не пострадает.

Артем сделал неплохое предложение, но я бы хотел немного дополнить его.

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

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