Здравствуйте! Подскажите, пожалуйста по какому алгоритму в системе создаются ключевые поля?
Дело в том, что я на Delphi пишу одну маленькую утилиту, которая будет получать доступ к физическим таблицам и вносить туда некоторые свои данные, возможно даже создавай своего "пустого контрагента" с присвоенным ему id, как рассчитать id?

Нравится

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

Марат, алгоритм прост - генерация нового Guid.

procedure TForm1.Button1Click(Sender: TObject);
var
  g: TGuid;
begin
  CreateGUID(g);
  ShowMessage(GUIDToString(g));
end;

Спасибо!

Марат, возможно, Вам будет полезная информация о том что, в версиях 3.0.х, создавая записи напрямую в таблицах, необходимо:
1. раздать права по-умолчанию на новую запись
2. если включено логгирование, не забыть внести соответствующую информацию в журнал изменений
При создании и изменении данных с использованием объектов Terrasoft, например из конфигурации, за выполнение этих функций отвечает ядро системы и делает это автоматически. Если действовать в обход ядра, то эту информацию необходимо помнить.

"Ключник Алексей" написал:2. если включено логгирование, не забыть внести соответствующую информацию в журнал изменений
При создании и изменении данных с использованием объектов Terrasoft, например из конфигурации, за выполнение этих функций отвечает ядро системы и делает это автоматически. Если действовать в обход ядра, то эту информацию необходимо помнить.

Алексей, с прав доступа - согласен, но с журналом изменения должен не согласится! Поскольку за это отвечает не ядро системы, а СУБД или точнее триггеры, которые создаются ядром системы при включение логирования!

Создаю запись в системы (в разделе с включенным логированием):

Изменяю данные вне системы:

Смотрю в журнал:

--
www.it-sfera.com.ua
Terrasoft Solution Partner

Вынужден и с первым пунктом не согласится! Права доступа "по-умолчанию" устанавливаются тоже на уровне СУБД...

--
www.it-sfera.com.ua
Terrasoft Solution Partner

Виталий, спасибо за замечание. Не указал в посте версии Terrasoft

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

Распространённая ситуация: надо очистить справочник от лишних записей. Когда это записи вроде «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. Скорость удаления записей практически не пострадает.

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

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

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