v7: Кто открыл документ Ключевые слова: 1с++, mssql, блокировка, документ, открыл
Пролог
Для больших 1с приложением когда пользователей больше 50 начинают очень
часто возникать проблемы как узнать кто открыл документ(справочник).
Обычно как всегда все происходит в самый неподходящий момент (законы Мерфи):
Клиент звонит менеджеру.Менеджер хочет внести небольшое изменение в счет( сделка
очень важная с хорошим клиентом на хорошую сумму) и обнаруживает что его счет
кто-то заблокировал. клиент висит на телефоне, домумент не открывается,
сделка под угрозой, менеджер в бешенстве.
Предлагается одно из решений для преодоления столь ужасной картины.
Постановка задачи
1. Обеспечить пользователям 1с просто обнаруживать кто блокировал конкретный документ.
2. Сделать невозможным сохранение документа после нахождения в нем свыше
заданного числа минут.
Поводом для написания статьи послужило:
"ОФФ: Прототип статьи. Решение проблемы файловых блокировок в 1С 7.7"
http://abelov.com/forum/f.php?ak=29369&pg=-1
Решение
Решение основана на 1с++. При начале работе 1с программы необходимо загрузить 1с++.
(Кому не нравиться 1с++ можно переписать через ado ).
В глобальном модуле надо добавить:
Функция SQLКтоОткрыл( ТекущЗнач) Экспорт
Если ПустоеЗначение(ТекущЗнач) = 1 Тогда Сообщить("Не выбрано значение");return 1; КонецЕсли;
meta1 = СоздатьОбъект("MetaDataWork");
rc = СоздатьОбъект("ODBCRecordSet");
Зн1 = "'" + meta1.ЗначениеВДлиннуюСтрокуБД(ТекущЗнач) + "'";
Стр_0 = "SELECT who,comp, convert( char,date, 113) from users_1c where id = " + Зн1;
Табл_2 = СоздатьОбъект("ТаблицаЗначений");
Табл_2.НоваяКолонка("Кто","Строка");
Табл_2.НоваяКолонка("Комр","Строка");
Табл_2.НоваяКолонка("ДатаВремя","Строка");
rc.Открыть(Стр_0, 0,0);
rc.УстТипыКолонок1С("Строка,Строка,Строка");
Попытка
rc.ПолучитьРезультатыВ_ТЗ(Табл_2,0 );
Исключение
Сообщить("не удалось обратиться к users_1c");
return 0;
КонецПопытки;
rc.Закрыть();
Если Табл_2.КоличествоСтрок() <= 0 Тогда
Сообщить("Объект никем не занят !!!");
return 1;
КонецЕсли;
Стр_1 = сокрЛП(Табл_2.ПолучитьЗначение(1,"Кто"));
Стр_2 = сокрЛП(Табл_2.ПолучитьЗначение(1,"Комр"));
Стр_3 = сокрЛП(Табл_2.ПолучитьЗначение(1,"ДатаВремя"));
Сообщить("" + Стр_1 + " комп " + Стр_2 + " открыл " + Стр_3);
return 1;
КонецФункции
Функция SQLОткрытьДокумент( Конт) Экспорт
//create table users_1c
//(
// id char (13),
// who char (50),
// comp char (50),
// date datetime,
// primary key (id)
//)
Если Конт.Форма.ТолькоПросмотр() = 1 Тогда
return 1;
КонецЕсли;
ТекДок = Конт.ТекущийДокумент();
Если ПустоеЗначение(ТекДок) = 1 Тогда return 1; КонецЕсли;
//Сообщить("ТекДок " + СокрЛП(ТекДок.НомерДок) + " от " + ТекДок.ДатаДок );
meta1 = СоздатьОбъект("MetaDataWork");
rc = СоздатьОбъект("ODBCRecordSet");
Зн1 = "'" + meta1.ЗначениеВДлиннуюСтрокуБД(ТекДок) + "'";
Зн2 = "'" + ИмяПользователя() + "'";
Зн3 = "'" + ИмяКомпьютера() + "'";
Стр_0 = "insert into users_1c ( id, who, comp, date) " +
" values ( " + Зн1 + "," + Зн2 + "," + Зн3 + "," + "GETDATE() )";
СделатьUpdate = 0;
Попытка
rc.Выполнить(Стр_0);
Исключение
СделатьUpdate = 1;
КонецПопытки;
Если СделатьUpdate = 1 Тогда
Стр_0 = "update users_1c set who = " + Зн2 + ", comp = " + Зн3 + " , date = GETDATE() " +
" where id = " + Зн1;
Попытка
rc.Выполнить(Стр_0);
Исключение
Сообщить("не смогли update ");
КонецПопытки;
КонецЕсли;
return 1;
КонецФункции
Функция SQLЗакрытьДокумент( Конт) Экспорт
ТекДок = Конт.ТекущийДокумент();
Если ПустоеЗначение(ТекДок) = 1 Тогда return 1; КонецЕсли;
//Сообщить("ТекДок " + СокрЛП(ТекДок.НомерДок) + " от " + ТекДок.ДатаДок );
meta1 = СоздатьОбъект("MetaDataWork");
rc = СоздатьОбъект("ODBCRecordSet");
Зн1 = "'" + meta1.ЗначениеВДлиннуюСтрокуБД(ТекДок) + "'";
Стр_0 = "delete from users_1c where id = " + Зн1;
Попытка
rc.Выполнить(Стр_0);
Исключение
КонецПопытки;
return 1;
КонецФункции
В каждом документе(спраовчнике) в модуле ПриОткрытии() необходимо добавить :
SQLОткрытьДокумент( Контекст);
В каждом документе(спраовчнике) в модуле ПриЗакрытии() необходимо добавить :
SQLЗакрытьДокумент( Контекст);
На кнопке в журнале документов кнопка. на кнопке функция
SQLКтоОткрыл( ТекущийДокумент)
Вроде все.
Таблица users_1c размещается в базе 1с ( можно разместить и в другой sql бд непринципиально)
параметры sql таблицы :
create table users_1c
(
id char (13),
who char (50),
comp char (50),
date datetime,
primary key (id)
)
Если таблица будет удалена в результате реструктаризации бд то ее надо востановить в ручном,
полуавтоматическом или автоматическом режиме.
Дальнейшее развитие
1. Все вышеизложенное можно адаптировать для 1с dbf версии.
2.Решение задачи запретить сохранять док через n минут) после всего вышеизложенного достаточно
тривиальная задача и оставлена в качестве упражнения.
3. Если в этом есть необходимость решение легко адаптируется и для v8.
Недостаток вышеизложенного. Если 1с приложение по каким либо причинам "упало"
то таблица users_1c не учитывает это.
Это можно улучшить следущим образом :
В таблицу user_1c добавляем два столбца spid,hostname.
Во время insert эти столбцы заполняем.
Во время выполнения отчетов ( у меня ктооткрыл )
Делаем нулевой шаг удаляем из таблицы user_1c все строки несуществующих
процессов.
т.е если в таблице user_1c есть <SPID1>, <HOSTNAME1>
и не существует такого значения <SPID1>, <HOSTNAME1>
в таблице master.dbo.sysprocesses (nolock) то эту строку удаляем из user_1c ( эта строка соответсвует свернувшимуся 1с приложению
Эпилог
Надеюсь, статья была полезной и Вы не зря потратили свое время, дочитав до этих строк.
Принимаются любые пожелания,коментарии, предложения, критика и.т.д.
02.06.2006 г.
с уважением Морев Андрей aka Z1 |