Почему зависают старые записи в регистре «Задания к распределению расчетов с клиентами» и как это исправить?

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

В современных конфигурациях 1С, таких как 1С:ERP Управление предприятием, 1С:Комплексная автоматизация (КА) 2 и 1С:Управление торговлей (УТ) 11, разработчики применили механизм отложенного проведения расчетов с клиентами и поставщиками. Этот механизм позволяет не перегружать систему тяжелыми вычислениями (такими как распределение авансов по FIFO, зачет оплат и расчет сроков долга) непосредственно в момент проведения первичных документов. Вместо этого система делает быструю запись в специальный регистр заданий, а само распределение выполняется позже в фоновом режиме.

Однако на практике многие администраторы и программисты 1С сталкиваются с ситуацией, когда в регистре сведений ЗаданияКРаспределениюРасчетовСКлиентами годами висят старые необработанные записи. Проанализируем, почему это происходит, чем это грозит системе и как решить эту проблему раз и навсегда.

Зачем нужен этот регистр и почему он должен быть пустым?

Регистр сведений ЗаданияКРаспределениюРасчетовСКлиентами — это технический буфер (очередь задач). При проведении накладной, платежного поручения или корректировки долга в этот регистр записывается информация о том, что по конкретному партнеру, организации и соглашению за определенный месяц требуется пересчитать взаиморасчеты.

Регламентное задание «Выполнение отложенных движений по расчетам с клиентами» регулярно запускается по расписанию, считывает накопленные задания из регистра, выполняет детальный расчет и удаляет отработанные записи из очереди. В идеальном состоянии этот регистр должен быть пустым (или содержать записи только за самый последний период, по которому еще ведется оперативная работа).

Если в регистре «зависли» старые записи (например, за прошлые годы), это серьезная техническая проблема. Она приводит к следующим последствиям:

  1. Катастрофическое падение производительности: При запуске процедуры распределения расчетов или при закрытии месяца система всегда ищет самую раннюю дату незавершенного задания (функцией МИНИМУМ(Задания.Месяц)). Если у вас «висит» забытая запись за 2019 год, система при каждом новом расчете или проведении текущих документов будет пытаться анализировать и пересчитывать цепочки расчетов начиная с 2019 года по сегодняшний день. Это создает колоссальную и бесполезную нагрузку на СУБД.
  2. Ошибки в печатных формах: Пользователи могут регулярно видеть раздражающие предупреждения: «Печатная форма сформирована по неактуальным данным. Необходимо актуализировать взаиморасчеты...» при попытке распечатать УПД или счета-фактуры.
  3. Блокировка удаления объектов: Наличие записей в регистре заданий не позволит корректно удалить помеченные на удаление документы, договоры или контрагентов, так как на них будут оставаться ссылочные связи.

---

Причина 1. Программный баг определения периода расчета в фоновом задании

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


Функция НачалоРасчета(ИмяРегистраЗаданий, ОкончаниеРасчета, АналитикиРасчета, НомерЗадания = 0)
    ТекстЗапроса = 
    "ВЫБРАТЬ
    |   МИНИМУМ(Задания.Месяц) КАК НачалоПериодаРасчета
    |ИЗ
    |   ИмяРегистраЗаданий КАК Задания
    |ГДЕ
    |   Задания.Месяц <= &ОкончаниеРасчета
    |   И (Задания.АналитикаУчетаПоПартнерам В (&АналитикиУчетаПоПартнерам)
    |       ИЛИ &ПоВсемАналитикам)
    |   И (Задания.Организация В (&Организации)
    |       ИЛИ &ПоВсемОрганизациям)
    |   И (Задания.НомерЗадания <= &НомерЗадания
    |       ИЛИ &ПоВсемНомерамЗаданий)
    |ИМЕЮЩИЕ
    |   НЕ (МИНИМУМ(Задания.Месяц) ЕСТЬ NULL)";
    
    ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ИмяРегистраЗаданий", "РегистрСведений." + ИмяРегистраЗаданий);
    Запрос = Новый Запрос(ТекстЗапроса);
    Запрос.УстановитьПараметр("ОкончаниеРасчета", ОкончаниеРасчета);
    // ... Установка остальных параметров ...
    Запрос.УстановитьПараметр("НомерЗадания", НомерЗадания);
    Запрос.УстановитьПараметр("ПоВсемНомерамЗаданий", НомерЗадания = 0);
    
    Выборка = Запрос.Выполнить.Выбрать;
    Возврат ?(Выборка.Следующий, Выборка.НачалоПериодаРасчета, КонецМесяца(ОкончаниеРасчета) + 1);
КонецФункции

В чем заключается проблема?

При запуске автоматического распределения через фоновое регламентное задание параметр НомерЗадания по умолчанию передается равным 0. В этом случае срабатывает условие отбора по всем номерам заданий. Однако при определенных условиях вычисление минимального периода расчета дает сбой, возвращая дату, равную ОкончаниеРасчета (например, 01.05.2019), в то время как в регистре реально есть более старые записи.

Затем в основном коде распределения запускается цикл по обработке периодов:


ПервыйПроход = Истина;
Пока НачалоПериода < ОкончаниеПериодаРасчета Цикл
    // Выполнение расчетов за месяц...
    // ...
КонецЦикла;

Если функция НачалоРасчета из-за некорректных параметров или логических коллизий вернула дату НачалоПериода, равную ОкончаниеПериодаРасчета, то условие цикла НачалоПериода < ОкончаниеПериодаРасчета возвращает ложь при первом же сравнении. Цикл распределения не отрабатывает ни разу! Из-за этого старые задания так и остаются лежать мертвым грузом в регистре сведений, а новые документы продолжают накапливаться.

Как решить этот баг программно?

Для исправления этой проблемы в коде (если вы используете старые релизы конфигураций до перехода на новые версии взаиморасчетов) можно принудительно инициализировать НомерЗадания, если он пришел равным нулю. Для этого перед вызовом основного запроса функции НачалоРасчета определим максимальный существующий номер задания в регистре:


// Доработка механизма определения номера задания
Если НомерЗадания = 0 И ИмяРегистраЗаданий = "ЗаданияКРаспределениюРасчетовСКлиентами" Тогда
    ЗапросНЗ = Новый Запрос;
    ЗапросНЗ.Текст = 
    "ВЫБРАТЬ
    |   МАКСИМУМ(Задания.НомерЗадания) КАК НомерЗадания
    |ИЗ
    |   РегистрСведений.ЗаданияКРаспределениюРасчетовСКлиентами КАК Задания";
    
    РезультатЗапроса = ЗапросНЗ.Выполнить();
    ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
    Если ВыборкаДетальныеЗаписи.Следующий() Тогда
        Если ЗначениеЗаполнено(ВыборкаДетальныеЗаписи.НомерЗадания) Тогда
            НомерЗадания = ВыборкаДетальныеЗаписи.НомерЗадания;
        КонецЕсли;
    КонецЕсли;
КонецЕсли;

Этот нехитрый прием позволяет корректно обойти ошибку инициализации параметров при запуске распределения из фонового режима и запустить цикл перерасчета по всем зависшим старым записям.

---

Причина 2. Ошибки в регламентных заданиях и блокировки периодов

Даже при полностью исправном коде задания могут зависнуть по более банальным «эксплуатационным» причинам:

  1. Падение регламентного задания по ошибке: Фоновое задание регулярно спотыкается на определенном документе (например, из-за незаполненной аналитики, пустой организации или дублирования записей в смежных регистрах). В результате транзакция откатывается, распределение прерывается, а задания остаются в регистре. Проанализируйте Журнал регистрации 1С на наличие ошибок по ключевым словам «Не удалось заблокировать регистр заданий» или «Запись не верна!».
  2. Даты запрета редактирования: При попытке распределить старые взаиморасчеты система пытается обновить движения исторических документов. Если прошлые периоды закрыты датой запрета изменения данных (даже для системного пользователя, под которым крутится служба 1С), запись в регистры блокируется, и расчет останавливается с ошибкой.
  3. Битые ссылки (<Объект не найден>): Если документы расчетов или договоры были удалены некорректно (без контроля ссылочной целостности, например, через внешние обработки очистки базы), в измерениях регистра заданий остаются битые ссылки. Алгоритм распределения не умеет их обрабатывать и аварийно завершает работу.

---

Пошаговый алгоритм наведения порядка в расчетах

Рассмотрим, как вернуть систему в рабочее состояние и очистить зависший регистр без вреда для сданной отчетности.

Шаг 1. Тестирование и исправление базы (ТИИ)

Если причиной зависания стали битые ссылки, необходимо выполнить логическую проверку базы данных. Перед процедурой обязательно создайте резервную копию базы!

Шаг 2. Ручная очистка регистра за закрытые периоды

Если прошлые годы (например, 2018–2022 гг.) уже полностью закрыты, проверены, декларации сданы, и вы не планируете ничего там пересчитывать, лучшим и самым безопасным решением будет полная ручная очистка регистра заданий за эти закрытые периоды. Это уберет «якорь», который заставлял фоновое задание при каждом запуске тратить ресурсы сервера на анализ старых периодов.

Для этого можно написать простую внешнюю обработку со следующим кодом удаления записей старше определенной даты:


ГраницаОчистки = '20230101000000'; // Все задания до 1 января 2023 года удаляем
НаборЗаписей = РегистрыСведений.ЗаданияКРаспределениюРасчетовСКлиентами.СоздатьНаборЗаписей();

// Выбираем только старые записи, чтобы удалить их
Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ РАЗЛИЧНЫЕ
|   Задания.Месяц КАК Месяц,
|   Задания.Организация КАК Организация,
|   Задания.АналитикаУчетаПоПартнерам КАК АналитикаУчетаПоПартнерам
|ИЗ
|   РегистрСведений.ЗаданияКРаспределениюРасчетовСКлиентами КАК Задания
|ГДЕ
|   Задания.Месяц < &ГраницаОчистки";
Запрос.УстановитьПараметр("ГраницаОчистки", ГраницаОчистки);
ТаблицаСтарыхЗаписей = Запрос.Выполнить().Выгрузить();

Для Каждого СтрокаЗаписи Из ТаблицаСтарыхЗаписей Цикл
    НаборЗаписей.Отбор.Месяц.Установить(СтрокаЗаписи.Месяц);
    НаборЗаписей.Отбор.Организация.Установить(СтрокаЗаписи.Организация);
    НаборЗаписей.Отбор.АналитикаУчетаПоПартнерам.Установить(СтрокаЗаписи.АналитикаУчетаПоПартнерам);
    
    // Записываем пустой набор, тем самым удаляя конкретную запись
    НаборЗаписей.Записать(Истина); 
КонецЦикла;

После удаления старых «хвостов» регламентное задание начнет выполняться мгновенно, так как ему больше не придется лопатить гигабайты старых исторических данных.

Шаг 3. Использование стандартных механизмов исправления разрывов

В конфигурациях 1С последних версий встроен специальный инструмент для борьбы со сбоями распределения. При закрытии месяца обязательно обратите внимание на регламентную операцию «Контроль и устранение разрывов расчетов с партнерами». Эта операция сравнивает оперативные регистры расчетов с детализированными и автоматически генерирует исправления, устраняя «подвисшие» остатки и распределения.

Шаг 4. Переход на «Новую архитектуру взаиморасчетов»

В версиях ERP / КА / УТ линеек 2.4 / 11.4 и старше механизмы расчетов с клиентами и поставщиками были разделены на разные регистры (ЗаданияКРаспределениюРасчетовСКлиентами и ЗаданияКРаспределениюРасчетовСПоставщиками).

В новых линейках решений (начиная с 2.5 / 11.5) фирма «1С» полностью переработала архитектуру. Теперь используется единый регистр сведений «Задания к распределению расчетов», а алгоритмы онлайн-распределения стали гораздо более отказоустойчивыми и оптимизированными по скорости. При обновлении конфигурации на современные релизы система автоматически перенесет данные, исправив большинство застарелых ошибок распределения прошлых периодов.

← На главную