Как исправить зависание 1С при проведении документов списания денежных средств в модуле ОперативныеВзаиморасчетыСервер?

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

В крупных ERP-решениях от 1С (таких как 1С:ERP Управление предприятием и 1С:Комплексная автоматизация 2) пользователи периодически сталкиваются с критической проблемой: при проведении платежных документов, например, Списание безналичных ДС, система зависает «намертво». Сеанс пользователя уходит в бесконечное ожидание, и единственным выходом остается принудительное завершение сеанса через консоль администрирования кластера 1С. При этом сам документ без проблем записывается, но транзакция проведения не может завершиться.

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

---

Шаг 1. Локализация места зависания (Диагностика отладчиком)

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

Проанализируем по шагам процесс поиска проблемного участка кода:

  1. Запустите информационную базу в режиме Отладка с подключенным сервером приложений (если база серверная, убедитесь, что на сервере включен параметр отладки -debug).
  2. В конфигураторе откройте меню Отладка -> Подключение... и убедитесь, что ваш сеанс успешно подключен.
  3. В режиме предприятия откройте проблемный документ Списание безналичных ДС и нажмите кнопку Провести.
  4. Как только интерфейс системы зависнет, немедленно перейдите в окно конфигуратора и нажмите на панели инструментов кнопку Пауза (Прервать) или выберите пункт меню Отладка -> Прервать.
  5. Конфигуратор остановит выполнение кода на той строке, которая обрабатывается процессором в данный момент. Посмотрите на стек вызовов. Повторите процедуру прерывания несколько раз, чтобы убедиться, что отладчик каждый раз останавливается в одном и том же цикле.

В рассматриваемом случае отладчик останавливается в общем модуле ОперативныеВзаиморасчетыСервер в процедуре ЗаполнитьОперативныеВзаиморасчеты. Мы попадаем внутрь следующего фрагмента кода:


Пока счПриход < ПланыОплат.Количество() ИЛИ счРасход < ПланыОплат.Количество() Цикл
    
    Если счРасход > ПланыОплат.Количество()-1 Тогда
        Если РаспределеноСтрок = 0 Тогда
            Прервать;
        Иначе
            РаспределеноСтрок = 0;
        КонецЕсли;
        счПриход = счПриход + 1;
        счРасход = 0;
    КонецЕсли;
    
    Если счПриход > ПланыОплат.Количество()-1 Тогда
        Прервать;
    КонецЕсли;
    
    Если ПланыОплат[счПриход].КОплате = 0 Тогда
        счПриход = счПриход + 1;
        Продолжить;
    КонецЕсли;
    
    Если ПланыОплат[счРасход].Оплачено = 0 Тогда
        счРасход = счРасход + 1;
        Продолжить;
    КонецЕсли;
    
    // ... дальнейшая логика распределения сумм
КонецЦикла;

---

Шаг 2. Выясняем причину бесконечного цикла

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

Обратите внимание на ключевые проверки условий:


Если ПланыОплат[счПриход].КОплате = 0 Тогда ...
Если ПланыОплат[счРасход].Оплачено = 0 Тогда ...

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

Если в поле КОплате оказывается отрицательное число (например, -100), то проверка КОплате = 0 возвращает значение Ложь. При этом дальнейшие ветки распределения сумм также не могут корректно обработать отрицательную величину. Индекс счПриход перестает увеличиваться, сбрасывается или зацикливается, а условие выхода из цикла Пока никогда не достигается. Программа уходит в бесконечный цикл, потребляя ресурсы процессора.

---

Шаг 3. Способы решения проблемы

Рассмотрим три альтернативных варианта решения данной проблемы: от быстрого исправления конфигурации до выравнивания данных на уровне СУБД.

Вариант 1. Программное исправление кода через расширение (Рекомендуется)

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

Для этого заменим строгое равенство нулю на оператор «меньше или равно» (<= 0). Выполним следующие действия по шагам:

  1. Создайте новое расширение конфигурации (или откройте существующее).
  2. Добавьте в расширение общий модуль ОперативныеВзаиморасчетыСервер.
  3. Найдите процедуру ЗаполнитьОперативныеВзаиморасчеты и перенесите её структуру в расширение, используя аннотацию &ИзменениеИКонтроль или &Вместо.
  4. Скорректируйте блоки кода, отвечающие за проверку остатков, следующим образом:

// Было в типовой конфигурации:
Если ПланыОплат[счПриход].КОплате = 0 Тогда
    счПриход = счПриход + 1;
    Продолжить;
КонецЕсли;

Если ПланыОплат[счРасход].Оплачено = 0 Тогда
    счРасход = счРасход + 1;
    Продолжить;
КонецЕсли;

// Стало в расширении (безопасный вариант):
Если ПланыОплат[счПриход].КОплате <= 0 Тогда
    счПриход = счПриход + 1;
    Продолжить;
КонецЕсли;

Если ПланыОплат[счРасход].Оплачено <= 0 Тогда
    счРасход = счРасход + 1;
    Продолжить;
КонецЕсли;

Аналогичные изменения необходимо внести во второй блок сопоставления (в конце цикла):


// Было:
Если ПланыОплат[счПриход].КОплате = 0 Тогда
    счПриход = счПриход + 1;
    счРасход = 0;
ИначеЕсли ПланыОплат[счРасход].Оплачено = 0 Тогда
    счРасход = счРасход + 1;
КонецЕсли;

// Стало:
Если ПланыОплат[счПриход].КОплате <= 0 Тогда
    счПриход = счПриход + 1;
    счРасход = 0;
ИначеЕсли ПланыОплат[счРасход].Оплачено <= 0 Тогда
    счРасход = счРасход + 1;
КонецЕсли;

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

Вариант 2. Поиск и исправление «битых» данных (Без изменения кода)

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

Напишем и выполним запрос к виртуальным таблицам остатков регистров планов оплат. Мы ищем записи с отрицательными значениями по ресурсам КОплате или Оплачено:


ВЫБРАТЬ
    "Расчеты с поставщиками" КАК Источник,
    ПланОплат.Регистратор КАК ДокументДвижения,
    ПланОплат.ОбъектРасчетов КАК ОбъектРасчетов,
    ПланОплат.КОплатеОстаток КАК СуммаКОплате,
    ПланОплат.ОплаченоОстаток КАК СуммаОплачено
ИЗ
    РегистрНакопления.РасчетыСПоставщикамиПланОплат.Остатки КАК ПланОплат
ГДЕ
    ПланОплат.КОплатеОстаток < 0
    ИЛИ ПланОплат.ОплаченоОстаток < 0

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    "Расчеты с клиентами",
    ПланОплатКлиенты.Регистратор,
    ПланОплатКлиенты.ОбъектРасчетов,
    ПланОплатКлиенты.КОплатеОстаток,
    ПланОплатКлиенты.ОплаченоОстаток
ИЗ
    РегистрНакопления.РасчетыСКлиентамиПланОплат.Остатки КАК ПланОплатКлиенты
ГДЕ
    ПланОплатКлиенты.КОплатеОстаток < 0
    ИЛИ ПланОплатКлиенты.ОплаченоОстаток < 0

Что делать с обнаруженными строками?

Вариант 3. Временный административный обход (Отключение онлайн-взаиморасчетов)

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

  1. Перейдите в раздел меню НСИ и администрирование -> Настройка НСИ и разделов -> Казначейство (или Взаиморасчеты в зависимости от версии конфигурации).
  2. Найдите настройку Ведение взаиморасчетов и временно переключите её из режима Онлайн в режим Офлайн (отложенное распределение).
  3. Проведите зависавший документ Списание безналичных ДС. Документ проведется мгновенно без зависаний, так как ресурсоемкий расчет распределения оплат по графикам будет перенесен из транзакции проведения в регламентное фоновое задание.
  4. После этого вы сможете спокойно заняться поиском причин расхождения данных и исправлением регистров накопления, после чего вернете настройку ведения взаиморасчетов в исходное состояние.

---

Резюме

Зависание при проведении документов списания безналичных денежных средств в решениях 1С:ERP и КА 2 — это классический пример архитектурной уязвимости кода к некорректным входящим данным. Разработчики не предусмотрели появление отрицательных значений в регистрах планов оплат, что приводит к бесконечным циклам в процедуре ЗаполнитьОперативныеВзаиморасчеты общего модуля ОперативныеВзаиморасчетыСервер.

Для долгосрочного решения мы рекомендуем использовать Вариант 1 (написание расширения), так как оно защитит вашу базу от подобных сбоев в будущем при возникновении любых учетных ошибок пользователей. Для быстрого исправления текущей ситуации отлично подойдет Вариант 3 (переключение на офлайн-расчеты) с последующей очисткой отрицательных остатков с помощью Варианта 2.

← На главную