Что делать, если 1С зависает при удалении или изменении документа с очень ранней датой?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

В практике администраторов и разработчиков 1С:Предприятие 8 периодически встречается ситуация, когда из-за опечатки пользователя в базу попадает документ с экстремальной датой, например, 01.04.0200 года вместо 2020-го. На первый взгляд проблема кажется тривиальной, но при попытке изменить дату, пометить документ на удаление или просто открыть его, программа «намертво» зависает. Рассмотрим подробнее, почему это происходит и как эффективно решить данную проблему, не прибегая к радикальным мерам.

Выясним причину: почему система зависает?

Разберем ситуацию с технической точки зрения. Платформа 1С хранит итоги регистров накопления и бухгалтерии в разрезе периодов (обычно это месяц). Когда в систему записывается проведенный документ с датой 0200 год, механизм управления итогами пытается актуализировать данные. Система начинает создавать записи в таблицах итогов для каждого месяца, начиная с 200-го года и до текущего момента. Простейший расчет показывает, что за 1800 лет программе необходимо обработать более 21 000 периодов для каждого регистра, по которому документ делает движения.

Проанализируем последствия: программа не «виснет» в обычном понимании этого слова. Она выполняет колоссальный объем операций записи в базу данных, пытаясь пересчитать итоги за почти два тысячелетия. В файловом варианте это гарантированно приводит к ожиданию в течение многих часов или даже суток, а в клиент-серверном варианте может вызвать переполнение логов транзакций СУБД или блокировку таблиц.

Способ 1. Программное изменение даты с флагом Загрузка

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

Рассмотрим пример кода, который поможет изменить дату документа без вызова лишних проверок:


НомерДок = "00000001"; // Номер злополучного документа
ДатаОшибочная = '02000401'; // 01.04.0200
ДокСсылка = Документы.РеализацияТоваровУслуг.НайтиПоНомеру(НомерДок, ДатаОшибочная);

Если Не ДокСсылка.Пустая() Тогда
    ДокОбъект = ДокСсылка.ПолучитьОбъект();
    ДокОбъект.Дата = ТекущаяДата(); // Устанавливаем корректную дату
    ДокОбъект.ОбменДанными.Загрузка = Истина;
    Попытка
        ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
        Сообщить("Дата успешно изменена");
    Исключение
        Сообщить("Ошибка при записи: " + ОписаниеОшибки());
    КонецПопытки;
КонецЕсли;

Важный момент: использование ОбменДанными.Загрузка = Истина отключает прикладные обработчики (такие как ПередЗаписью или ПриЗаписи в модуле объекта), но на уровне платформы пересчет итогов все равно может начаться, если документ остается проведенным. Поэтому, если этот метод не помогает и система все равно зависает, переходим к более решительным действиям.

Способ 2. Отключение итогов регистров (наиболее эффективный)

Если программная запись не помогает, значит, платформа блокирует операцию из-за попытки пересчитать итоги. Чтобы мгновенно изменить или удалить документ, нам нужно временно «запретить» системе обновлять таблицы итогов. Посмотрим на алгоритм действий:

  1. Определим, по каким регистрам документ делает движения (это можно посмотреть в конфигураторе или через «Движения документа»).
  2. Программным кодом отключим использование итогов для этих регистров.
  3. Выполним удаление документа или изменение его даты.
  4. Включим итоги обратно и запустим их пересчет.

Проанализируем пример кода для временного отключения итогов регистра накопления ВыручкаИПродажи:


// 1. Отключаем использование итогов
РегистрыНакопления.ВыручкаИПродажи.УстановитьИспользованиеИтогов(Ложь);

// 2. Теперь удаляем документ или меняем дату
// В этом состоянии 1С не будет пытаться пересчитать 1800 лет итогов
ОбъектДок = Документы.ИмяДокумента.НайтиПоНомеру("Номер", '02000401').ПолучитьОбъект();
Если ОбъектДок <> Неопределено Тогда
    ОбъектДок.УстановитьПометкуУдаления(Истина);
КонецЕсли;

// 3. Включаем итоги обратно
РегистрыНакопления.ВыручкаИПродажи.УстановитьИспользованиеИтогов(Истина);
// 4. Обязательно запускаем пересчет за актуальный период
РегистрыНакопления.ВыручкаИПродажи.ПересчитатьИтоги();

Внимание: Если документ делает движения по многим регистрам (что типично для УТ 11 или ERP), итоги нужно отключить для всех этих регистров. В противном случае система «зависнет» на первом же включенном регистре.

Способ 3. Прямое удаление через СУБД (для опытных администраторов)

В случае использования клиент-серверного варианта (SQL), можно прибегнуть к прямому удалению записей из таблиц базы данных. Это самый быстрый способ, так как он полностью игнорирует логику 1С и механизмы пересчета итогов.

Разберем последовательность действий в СУБД:

Рекомендации по профилактике

Чтобы избежать подобных ситуаций в будущем, рекомендуется установить Дату запрета редактирования. Даже если вы только начали вести учет, установите дату запрета на начало текущего года или прошлый месяц. Это заблокирует возможность записи документов в «глубокое прошлое» из-за случайных опечаток пользователей.

Также в некоторых конфигурациях можно программно ограничить минимально возможную дату в модуле сеанса или через подписки на события ПередЗаписью для всех документов. Рассмотрим пример такой проверки:


Процедура ПриЗаписиДокументаПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
    Если Источник.Дата < '20000101' Тогда
        Сообщить("Введена некорректная дата (ранее 2000 года)!");
        Отказ = Истина;
    КонецЕсли;
КонецПроцедуры

Подведем итог: если вы столкнулись с «зависшим» документом из 200-го года, не паникуйте и не спешите перезагружать сервер. Используйте метод с программным отключением итогов УстановитьИспользованиеИтогов(Ложь) — это самый безопасный и эффективный способ вернуть базу в рабочее состояние.

← На главную