При развитии и доработке конфигураций 1С разработчики часто сталкиваются с типовой задачей: в существующий документ добавляется новый реквизит (например, направление деятельности, дополнительная аналитика или ответственный менеджер). С момента добавления новые документы заполняются корректно, однако исторические данные за прошлые периоды остаются пустыми.
Для построения отчетов и корректного анализа данных эти реквизиты в старых документах необходимо заполнить. Сложность заключается в том, что документы прошлых периодов закрыты для редактирования, по ним сдана отчетность, а в модулях объектов и подписках на события прописана сложная логика (проверки, проведение по регистрам, отправка уведомлений, интеграции). Простое перепроведение или даже перезапись документов штатными средствами «в лоб» может привести к непредсказуемым изменениям в учете или критическим ошибкам.
В этой статье мы подробно разберем, почему идея прямой записи данных в СУБД (через SQL-запросы UPDATE) является крайне опасным путем, и рассмотрим безопасные, легальные альтернативы на уровне платформы 1С.
На первый взгляд кажется, что самым простым и быстрым решением является выполнение прямого SQL-запроса к таблице базы данных на сервере СУБД (MS SQL, PostgreSQL и т.д.). Например, написать простой скрипт обновления колонки в таблице документа. Однако такое вмешательство несёт в себе колоссальные риски и может привести к разрушению целостности базы данных. Разберем основные причины, почему этот метод использовать категорически запрещено:
rphost) и клиентские сеансы активно кэшируют считываемые данные. Если вы измените значение реквизита напрямую в таблице SQL, сервер 1С об этом просто не узнает. Пользователи продолжат видеть старые (пустые) значения до тех пор, пока кэш не будет принудительно очищен или пока не перезапустится служба сервера «1С:Предприятие»._Version): Каждая таблица документов или справочников в СУБД содержит системное поле версии (обычно _Version типа timestamp или целочисленного типа). При изменении строки напрямую в СУБД версия объекта не обновляется по алгоритмам платформы. В результате при первой же попытке пользователя перезаписать этот документ штатно в режиме предприятия возникнет ошибка блокировки: «Данные были изменены или удалены другим пользователем»._RRef типа binary(16)). Платформа 1С записывает уникальные идентификаторы (GUID) со специфическим, «перевернутым» порядком байт. Попытка записать строковый или стандартный GUID напрямую через SQL приведет к бинарному несоответствию, и в 1С вместо ссылки вы получите битый объект со строкой «<Объект не найден...>».Tool_1CD) для прямой записи в файловую базу сопряжена с критическим риском безвозвратной порчи структуры всего файла базы данных. Файловая СУБД не обладает транзакционной устойчивостью к внешнему вмешательству.Поэтому мы полностью отказываемся от идеи прямого редактирования СУБД и переходим к штатным, безопасным механизмам платформы.
Платформа 1С предоставляет штатный механизм для записи объектов «как есть», без выполнения проверок, проведения и сопутствующих обработчиков. Для этого используется свойство объекта ОбменДанными.Загрузка. При установке этого свойства в значение Истина платформа понимает, что объект переносится (например, в рамках обмена данными) и его нужно просто сохранить в базу данных в текущем виде.
Для реализации этого способа во всех обработчиках событий (таких как ПередЗаписью, ПриЗаписи в модуле документа, а также во всех подписках на эти события) в первой же строке кода должна стоять проверка:
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
Если в вашей конфигурации (особенно если она нетиповая и создавалась с нуля) такой проверки нет, то это считается серьезным техническим долгом. Пропишите эту проверку во всех обработчиках и подписках. Это стандарт разработки 1С, который в будущем позволит безболезненно выполнять любые групповые обработки данных.
После того как вы убедились в наличии проверки свойства загрузки, мы можем запустить внешнюю обработку со следующим кодом для заполнения реквизита:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Реализация.Ссылка КАК Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Дата < &ДатаНачалаДействияРеквизита
| И Реализация.НовыйРеквизит = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)"; // Укажите ваш пустой тип
Запрос.УстановитьПараметр("ДатаНачалаДействияРеквизита", ДатаНачалаЗаполнения);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Получаем объект документа для изменения
ДокументОбъект = ВыборкаДетальныеЗаписи.Ссылка.ПолучитьОбъект();
// Заполняем наш новый реквизит рассчитанным значением
ДокументОбъект.НовыйРеквизит = ВычислитьЗначениеРеквизита(ДокументОбъект);
// Включаем режим загрузки для обхода обработчиков и подписок
ДокументОбъект.ОбменДанными.Загрузка = Истина;
Попытка
// Записываем документ БЕЗ проведения, только сохраняя изменения
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
Исключение
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Ошибка записи документа " + ДокументОбъект.Ссылка + ": " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
При таком подходе документ просто обновит значения полей в таблице базы данных (включая правильный расчет версии объекта и обновление кэша сервера), не делая никаких движений по регистрам накопления или бухгалтерии.
Если ваша база данных нетиповая, в ней полностью отсутствуют проверки на ОбменДанными.Загрузка, а обработчиков и подписок настолько много, что внедрять эту проверку в каждый модуль физически нет времени (или это нецелесообразно на данном этапе), можно прибегнуть к разовому практическому приему:
ПередЗаписью и ПриЗаписи для нужного документа.ОбменДанными.Загрузка можно не использовать, так как обработчики временно отключены). Запись выполняйте строго в режиме РежимЗаписиДокумента.Запись.Этот метод абсолютно безопасен для целостности структуры СУБД, так как запись происходит через штатное ядро платформы 1С, но требует временной остановки работы пользователей для монопольного обновления конфигурации.
Рассмотрим ситуацию, когда изменять документы прошлых (закрытых) периодов категорически запрещено требованиями безопасности, аудита или жесткими регламентами предприятия. Любое изменение физической таблицы документа (даже без проведения) оставляет след в системных журналах и меняет дату модификации файла/объекта.
В этом случае отличной архитектурной альтернативой является отказ от добавления нового реквизита непосредственно внутрь самого документа. Вместо этого мы используем независимый регистр сведений:
ДополнительныеСвойстваДокументов).ДокументСсылка (тип: ссылка на ваш документ).При построении отчетов мы сможем легко получать это свойство, используя простое левое соединение в запросе:
ВЫБРАТЬ
Реализация.Ссылка КАК ДокументСсылка,
Реализация.Номер КАК Номер,
Реализация.Дата КАК Дата,
ЕСТЬNULL(ДопСвойства.ЗначениеСвойства, &ЗначениеПоУмолчанию) КАК НовыйРеквизит
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДополнительныеСвойстваДокументов КАК ДопСвойства
ПО Реализация.Ссылка = ДопСвойства.ДокументСсылка
Если в вашей системе используется библиотека стандартных подсистем (БСП), то аналогичный функционал уже заложен в механизме «Дополнительные реквизиты и сведения». Сведения хранятся в специальном регистре сведений и могут быть выведены в любые отчеты без изменения метаданных документов.
При необходимости заполнить реквизиты исторических документов прошлых периодов всегда делайте выбор в пользу безопасности данных и стабильности системы. Прямая запись в БД через SQL-запросы — это крайняя мера, приводящая к нарушению лицензии, рассинхронизации кэша сервера и ошибкам блокировок.
Используйте штатный механизм ОбменДанными.Загрузка = Истина, внедряйте проверки в модули объектов и подписки, либо выносите новую аналитику в независимые регистры сведений. Это позволит вам эффективно решить задачу, сохранив целостность базы данных и спокойствие пользователей.