В практике администраторов и разработчиков 1С:Предприятие 8 периодически встречается ситуация, когда из-за опечатки пользователя в базу попадает документ с экстремальной датой, например, 01.04.0200 года вместо 2020-го. На первый взгляд проблема кажется тривиальной, но при попытке изменить дату, пометить документ на удаление или просто открыть его, программа «намертво» зависает. Рассмотрим подробнее, почему это происходит и как эффективно решить данную проблему, не прибегая к радикальным мерам.
Разберем ситуацию с технической точки зрения. Платформа 1С хранит итоги регистров накопления и бухгалтерии в разрезе периодов (обычно это месяц). Когда в систему записывается проведенный документ с датой 0200 год, механизм управления итогами пытается актуализировать данные. Система начинает создавать записи в таблицах итогов для каждого месяца, начиная с 200-го года и до текущего момента. Простейший расчет показывает, что за 1800 лет программе необходимо обработать более 21 000 периодов для каждого регистра, по которому документ делает движения.
Проанализируем последствия: программа не «виснет» в обычном понимании этого слова. Она выполняет колоссальный объем операций записи в базу данных, пытаясь пересчитать итоги за почти два тысячелетия. В файловом варианте это гарантированно приводит к ожиданию в течение многих часов или даже суток, а в клиент-серверном варианте может вызвать переполнение логов транзакций СУБД или блокировку таблиц.
Попробуем сначала самый простой программный метод. Интерактивное изменение даты в форме документа часто невозможно, так как при открытии или записи формы срабатывают типовые проверки и обработчики событий, которые инициируют пересчет. Программная запись объекта позволяет обойти часть этих механизмов с помощью свойства ОбменДанными.Загрузка.
Рассмотрим пример кода, который поможет изменить дату документа без вызова лишних проверок:
НомерДок = "00000001"; // Номер злополучного документа
ДатаОшибочная = '02000401'; // 01.04.0200
ДокСсылка = Документы.РеализацияТоваровУслуг.НайтиПоНомеру(НомерДок, ДатаОшибочная);
Если Не ДокСсылка.Пустая() Тогда
ДокОбъект = ДокСсылка.ПолучитьОбъект();
ДокОбъект.Дата = ТекущаяДата(); // Устанавливаем корректную дату
ДокОбъект.ОбменДанными.Загрузка = Истина;
Попытка
ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
Сообщить("Дата успешно изменена");
Исключение
Сообщить("Ошибка при записи: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
Важный момент: использование ОбменДанными.Загрузка = Истина отключает прикладные обработчики (такие как ПередЗаписью или ПриЗаписи в модуле объекта), но на уровне платформы пересчет итогов все равно может начаться, если документ остается проведенным. Поэтому, если этот метод не помогает и система все равно зависает, переходим к более решительным действиям.
Если программная запись не помогает, значит, платформа блокирует операцию из-за попытки пересчитать итоги. Чтобы мгновенно изменить или удалить документ, нам нужно временно «запретить» системе обновлять таблицы итогов. Посмотрим на алгоритм действий:
Проанализируем пример кода для временного отключения итогов регистра накопления ВыручкаИПродажи:
// 1. Отключаем использование итогов
РегистрыНакопления.ВыручкаИПродажи.УстановитьИспользованиеИтогов(Ложь);
// 2. Теперь удаляем документ или меняем дату
// В этом состоянии 1С не будет пытаться пересчитать 1800 лет итогов
ОбъектДок = Документы.ИмяДокумента.НайтиПоНомеру("Номер", '02000401').ПолучитьОбъект();
Если ОбъектДок <> Неопределено Тогда
ОбъектДок.УстановитьПометкуУдаления(Истина);
КонецЕсли;
// 3. Включаем итоги обратно
РегистрыНакопления.ВыручкаИПродажи.УстановитьИспользованиеИтогов(Истина);
// 4. Обязательно запускаем пересчет за актуальный период
РегистрыНакопления.ВыручкаИПродажи.ПересчитатьИтоги();
Внимание: Если документ делает движения по многим регистрам (что типично для УТ 11 или ERP), итоги нужно отключить для всех этих регистров. В противном случае система «зависнет» на первом же включенном регистре.
В случае использования клиент-серверного варианта (SQL), можно прибегнуть к прямому удалению записей из таблиц базы данных. Это самый быстрый способ, так как он полностью игнорирует логику 1С и механизмы пересчета итогов.
Разберем последовательность действий в СУБД:
_Document123) и его движений (например, _AccumReg456).DELETE по ссылке (UID) документа во всех найденных таблицах.Чтобы избежать подобных ситуаций в будущем, рекомендуется установить Дату запрета редактирования. Даже если вы только начали вести учет, установите дату запрета на начало текущего года или прошлый месяц. Это заблокирует возможность записи документов в «глубокое прошлое» из-за случайных опечаток пользователей.
Также в некоторых конфигурациях можно программно ограничить минимально возможную дату в модуле сеанса или через подписки на события ПередЗаписью для всех документов. Рассмотрим пример такой проверки:
Процедура ПриЗаписиДокументаПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
Если Источник.Дата < '20000101' Тогда
Сообщить("Введена некорректная дата (ранее 2000 года)!");
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Подведем итог: если вы столкнулись с «зависшим» документом из 200-го года, не паникуйте и не спешите перезагружать сервер. Используйте метод с программным отключением итогов УстановитьИспользованиеИтогов(Ложь) — это самый безопасный и эффективный способ вернуть базу в рабочее состояние.