Что за корень? О чем это вообще?
Вы, как и я, все еще работаете с хранилищем?
Тогда, думаю, иногда в помещении, где сидят программисты (хотя кого я обманываю, программисты давно уже не сидят в офисах компаний, - в чате скайпа, телеграмма и тд) слышны возгласы - "отдай корень!".
Объясню для людей, не связанных непосредственно с программированием на 1С, что же означает эта загадочная фраза.
Хранилище конфигурации 1С - это инструмент групповой разработки. Работают с хранилищем следующим образом: захватывают какой-либо объект, редактируют, потом отдают его в хранилище. Хранилище помечает уже захваченные объекты и не дает возможности захватить их другим пользователям. С одной стороны это очень удобно: конфликты при помещении измененных объектов невозможны - один объект может редактироваться в один момент только одним пользователем. С другой стороны это же рождает и самый большой недостаток хранилища - невозможность работы с одним объектом нескольких пользователей, например в случае доработки разных методов в одном большом модуле.
Корень конфигурации - это самый верхний ее узел. Только захватив корень, мы можем добавить в конфигурацию новые общие модули, документы, справочники, регистры и подобное. Только захватив корень можно изменить настройки поддержки конфигурации. Соответственно, если корень захвачен одним программистом, другой программист не может добавить новые объекты или снять что-то с поддержки.
Потому то и всплывает эта фраза - отдай корень, мне нужно тоже что-то добавить.
Для каких-то команд это вообще не проблема - нет трудностей кинуть клич - "Отдай корень", дождаться, когда корень будет освобожден, захватить его и сделать свои дела.
Но когда команда большая, коммуникация может быть затруднена. Кроме того, человек, захвативший корень, может быть в отпуске. Часто в регламентах работы программистов уделяют внимание этой проблеме и рождают строки типа "Недопустимо захватывать корень конфигурации на время более 2 часов". Но как и большая часть регламентов, этот пункт соблюдается слабо. Корень нужен редко, да и кто будет следить за тем, сколько времени он захвачен?
Дело в том, что у нас нет технических средств, чтобы увидеть кто и что захватил в хранилище. Информация о захвате доступна только в конфигураторе. Если пользователь заходит в конфигуратор и авторизуется в хранилище, в дереве конфигурации становится доступна кнопка "Захваченные объекты", которая отфильтрует дерево по захваченным этим пользователем объектам.
Также в разделе меню конфигуратора Конфигурация - Хранилище конфигурации - Хранилище открывается окно конфигурации, в котором доступен отбор по всем захваченным объектам, или же по объектам, захваченным конкретным пользователем.
Несмотря на то, что разработчики программы 1С дали нам возможность работать с конфигуратором и хранилищем из командной строки, возможности получения информации о захвате не предусмотрели. Что же делать?
Если подходящего инструмента нет - сделаем его
Есть прекрасная библиотека на OneScript - v8storage - библиотека для упрощения работы с Хранилищем 1С. Так как она опирается на конфигуратор, чуда она не делает - таблицу захваченных объектов с ее помощью не получить. Однако можно попытаться захватить всю конфигурацию рекурсивно и по "выхлопу" работы команды захвата понять, что какие-то объекты не захвачены, так как были захвачены ранее.
Выглядит сложно, да и захватывать рекурсивно конфигурацию - чрезмерные затраты времени и ресурсов.
И есть библиотека на OneScript - tool1cd. Это программная скриптовая обертка для утилиты чтения файловых баз данных tool1cd от awa. Она умеет читать хранилище. В файле хранилища имеются таблица USERS, в которой есть поля USERID и имя пользователя, таблица OBJECTS, в которой есть поля OBJID - идентификатор объекта метаданных, REVISORID - идентификатор пользователя, REVISEDATE - дата захвата и REVISED - признак захвата объекта. Связать идентификатор объекта метаданных с именем можно с помощью таблицы HISTORY, в которой есть идентификаторы OBJID и PARENTID, а также OBJNAME - имя объекта.
На основе этих данных я сделал библиотеку - storagereader, которая берет на себя всю рутину по чтению файлов хранилища и определению захваченных объектов.
Библиотека для OneScript storagereader
Установка
Локальная установка
Скачиваем пакет из релизов storagereader и устанавливаем через opm
opm install -f .\storagereader-1.0.0.ospx
Установка из хаба
opm install storagereader
Подключение
#Использовать storagereader
Описание и методы
После подключения библиотеки доступно создание объекта ЧтениеХранилища. Затем нужно вызвать метод объекта ЧтениеХранилища - ПрочитатьХранилище().
ЧтениеХранилища = Новый ЧтениеХранилища(Хранилище, ВыгрузкаКонфигурации);
ЧтениеХранилища.ПрочитатьХранилище();
Создавать объект можно со следующими параметрами:
- Хранилище - тут нужно указать каталог хранилища 1С
- ВыгрузкаКонфигурации - тут можно указать каталог выгрузки конфигурации в исходный код (зачем он нужен - опишу ниже)
ПрочитатьХранилище() считывает переданные параметры, проверяет их, и формирует таблицу значений, в которой содержатся данные о захваченных объектах.
Если вызвать метод объекта ЧтениеХранилища ТаблицаЗахватов() вернется эта таблица значений.
ТаблицаЗахватов = ЧтениеХранилища.ТаблицаЗахватов();
Колонки у этой таблицы значений следующие:
- Метаданные - Строка - описание объекта метаданных
- Пользователь - Строка - Пользователь, как он задан в хранилище
- ДатаЗахвата - Дата - Дата захвата объекта
Если не передавать второй параметр при создании ЧтениеХранилища, метаданные вернутся в следующем виде (примерно так они выглядят в БД хранилища):
- Конфигурация.СуперФорма - общая форма с именем СуперФорма
- Конфигурация - корень конфигурации
- Конфигурация.Справочник1.ФормаЭлемента - форма справочника Справочник1
- Конфигурация.Справочник1 - сам справочник Справочник1
Это данные о захвате объектов с тестового хранилища, приведенного в изображении выше.
Немного непривычно, особенно для тех, кто выгружает изменения конфигурации в исходный код с помощью gitsync и подобного. Покопавшись в идентификаторах объектов метаданных в хранилище я выяснил, что они совпадают с таковыми в файле ConfigDumpInfo.xml.
Поэтому специально для того, чтобы таблица захватов была более похожа на данные выгрузки в исходный код, я добавил обработку параметра ВыгрузкаКонфигурации, если он заполнен - метаданные берутся из файла ConfigDumpInfo.xml в каталоге выгрузки и выглядят так:
- CommonForm.СуперФорма - общая форма с именем СуперФорма
- Configuration.ТестоваяКонфигурация - корень конфигурации
- Catalog.Справочник1.Form.ФормаЭлемента - форма справочника Справочник1
- Catalog.Справочник1;Администратор - сам справочник Справочник1
Единственная сложность - каталог с исходниками должен быть актуальным, так как если в хранилище добавили новый объект, идентификаторов которого нет в каталоге с исходниками, метаданные этого объекта будут пустыми. Так как скрипты с использованием этой библиотеки крутятся на сервере, на котором выгружаются исходники, каталоги с исходниками у меня всегда актуальны.
Также у ЧтениеХранилища есть метод КореньЗахвачен(), который возвращает структуру с информацией о захвате корня хранилища.
ДанныеОЗахватеКорня = ЧтениеХранилища.КореньЗахвачен();
Поля у этой структуры следующие:
- Захвачен - Булево
- Длительность - Число - Количество секунд, прошедшее с момента захвата в хранилище корня
- Пользователь - Строка - Пользователь, как он задан в хранилище
Примеры
Напишем скрипт, который отправит в группу телеграмм уведомление, что корень захвачен больше двух часов. Запускаем его регламентом и вся команда знает, кто захватил корень. Вот оно - фраза "Отдай корень" выдается автоматически, не нужно тратить на это свои силы.
#Использовать storagereader
#Использовать messenger
Процедура СообщитьЧерезТелеграм(ТекстСообщения)
Мессенджер = Новый Мессенджер();
Мессенджер.ИнициализироватьТранспорт("telegram", Новый Структура("Логин", "хххххххххххххххххххххххххххххх"));
Мессенджер.ОтправитьСообщение("telegram", "00000000", ТекстСообщения);
КонецПроцедуры
ЧтениеХранилища = Новый ЧтениеХранилища(ПутьКХранилищу);
ЧтениеХранилища.ПрочитатьХранилище();
ДанныеОЗахватеКорня = ЧтениеХранилища.КореньЗахвачен();
Если ДанныеОЗахватеКорня.Захвачен И ДанныеОЗахватеКорня.Длительность > 7200 Тогда
ТекстСообщения = СтрШаблон("Корень захвачен %1 часов назад пользователем %2", Окр(ДанныеОЗахватеКорня.Длительность / 3600), ДанныеОЗахватеКорня.Пользователь);
СообщитьЧерезТелеграм(ТекстСообщения);
КонецЕсли;
Напишем скрипт, который сохраняет таблицу захваченных объектов в текстовый файл и отправляет ее в гит - так я вижу "жизнь" хранилища - вот захватили объект, поработали с ним, отпустили, вот появилась версия хранилища с измененным объектом. Также при распределении задач не обязательно иметь открытый конфигуратор - из этой таблицы видно, что кем захвачено.
#Использовать storagereader
#Использовать gitrunner
Процедура ЗаписатьТаблицу(ТаблицаСтатистики, ИмяФайла)
МассивСтрок = Новый Массив;
МассивОписанияКолонок = Новый Массив;
Для каждого Колонка Из ТаблицаСтатистики.Колонки Цикл
МассивОписанияКолонок.Добавить(Колонка.Имя);
КонецЦикла;
МассивСтрок.Добавить(СтрСоединить(МассивОписанияКолонок, ";"));
Для Каждого Строка Из ТаблицаСтатистики Цикл
ОписаниеСтроки = Новый Массив;
Для каждого Колонка Из ТаблицаСтатистики.Колонки Цикл
ОписаниеСтроки.Добавить(Строка[Колонка.Имя]);
КонецЦикла;
МассивСтрок.Добавить(СтрСоединить(ОписаниеСтроки, ";"));
КонецЦикла;
ИтоговыйТекст = СтрСоединить(МассивСтрок, Символы.ПС);
ЗаписьТекста = Новый ЗаписьТекста(ИмяФайла);
ЗаписьТекста.Записать(ИтоговыйТекст);
ЗаписьТекста.Закрыть();
КонецПроцедуры
Процедура КоммитГит(КаталогГитРепозитария, ИмяФайлаОписания)
Дата = ТекущаяДата();
АвторКоммита = "Служебный автор <nnnnn@git.ru>";
ГитРепозиторий = Новый ГитРепозиторий();
ГитРепозиторий.УстановитьРабочийКаталог(КаталогГитРепозитария);
ГитРепозиторий.ДобавитьФайлВИндекс(КаталогГитРепозитария);
СтатусРепо = ГитРепозиторий.Статус();
Если СтрНайти(СтатусРепо, ИмяФайлаОписания) > 0 Тогда
ГитРепозиторий.Закоммитить("Изменение таблицы захваченных объектов в хранилище...", , , АвторКоммита, Дата, АвторКоммита, Дата);
КонецЕсли;
КонецПроцедуры
ЧтениеХранилища = Новый ЧтениеХранилища(ПутьКХранилищу);
ЧтениеХранилища.ПрочитатьХранилище();
ТаблицаЗахватов = ЧтениеХранилища.ТаблицаЗахватов();
ИмяФайлаТаблицы = "Захваченные объекты.csv";
Каталог = "КаталогТаблицЗахвата";
ЗаписатьТаблицу(ТаблицаЗахватов, ОбъединитьПути(Каталог, ИмяФайлаТаблицы));
КоммитГит(Каталог, ИмяФайлаТаблицы);
Благодарности
Андрею Овсянкину, создателю OneScript
И Никите Иванченко, у которого на инфостарте 2023 были великолепные доклад и мастер-класс по OneScript, его доклад уже опубликован, посмотрите его, он определенно стоит внимания.
И всем в чате телеграм oscript_library