В крупных ERP-решениях от 1С (таких как 1С:ERP Управление предприятием и 1С:Комплексная автоматизация 2) пользователи периодически сталкиваются с критической проблемой: при проведении платежных документов, например, Списание безналичных ДС, система зависает «намертво». Сеанс пользователя уходит в бесконечное ожидание, и единственным выходом остается принудительное завершение сеанса через консоль администрирования кластера 1С. При этом сам документ без проблем записывается, но транзакция проведения не может завершиться.
В этой статье мы подробно разберем, почему возникает данная проблема, как локализовать место зависания с помощью инструментов разработчика, и рассмотрим несколько эффективных способов решения — от изменения программного кода до исправления ошибочных данных в информационной базе без снятия конфигурации с поддержки.
---
Когда система зависает в бесконечном цикле, стандартный инструмент Замер производительности может оказаться неэффективным, так как замер фиксирует время выполнения операций, а бесконечный цикл просто заполнит всю память и не выдаст финальный результат. В такой ситуации лучшим решением является использование кнопки Пауза (Прерывание) в конфигураторе.
Проанализируем по шагам процесс поиска проблемного участка кода:
-debug).Списание безналичных ДС и нажмите кнопку Провести.В рассматриваемом случае отладчик останавливается в общем модуле ОперативныеВзаиморасчетыСервер в процедуре ЗаполнитьОперативныеВзаиморасчеты. Мы попадаем внутрь следующего фрагмента кода:
Пока счПриход < ПланыОплат.Количество() ИЛИ счРасход < ПланыОплат.Количество() Цикл
Если счРасход > ПланыОплат.Количество()-1 Тогда
Если РаспределеноСтрок = 0 Тогда
Прервать;
Иначе
РаспределеноСтрок = 0;
КонецЕсли;
счПриход = счПриход + 1;
счРасход = 0;
КонецЕсли;
Если счПриход > ПланыОплат.Количество()-1 Тогда
Прервать;
КонецЕсли;
Если ПланыОплат[счПриход].КОплате = 0 Тогда
счПриход = счПриход + 1;
Продолжить;
КонецЕсли;
Если ПланыОплат[счРасход].Оплачено = 0 Тогда
счРасход = счРасход + 1;
Продолжить;
КонецЕсли;
// ... дальнейшая логика распределения сумм
КонецЦикла;
---
Проанализируем логику работы этого цикла. Алгоритм предназначен для сопоставления планируемых приходов и расходов по оплате (так называемая Новая архитектура взаиморасчетов, работающая в режиме онлайн). Он обходит таблицу значений ПланыОплат с помощью двух индексов-счетчиков: счПриход и счРасход.
Обратите внимание на ключевые проверки условий:
Если ПланыОплат[счПриход].КОплате = 0 Тогда ...
Если ПланыОплат[счРасход].Оплачено = 0 Тогда ...
Разработчики фирмы 1С исходили из математической модели, в которой показатели КОплате и Оплачено могут быть либо строго положительными (когда есть остаток долга/платежа), либо равными нулю (когда все распределено). Однако реальный учет вносит свои коррективы. В результате некорректного перехода на новые релизы, нарушения последовательности проведения документов, ручных корректировок регистров или сбоев при обновлении данных, в регистрах накопления РасчетыСПоставщикамиПланОплат или РасчетыСКлиентамиПланОплат могут образоваться отрицательные остатки.
Если в поле КОплате оказывается отрицательное число (например, -100), то проверка КОплате = 0 возвращает значение Ложь. При этом дальнейшие ветки распределения сумм также не могут корректно обработать отрицательную величину. Индекс счПриход перестает увеличиваться, сбрасывается или зацикливается, а условие выхода из цикла Пока никогда не достигается. Программа уходит в бесконечный цикл, потребляя ресурсы процессора.
---
Рассмотрим три альтернативных варианта решения данной проблемы: от быстрого исправления конфигурации до выравнивания данных на уровне СУБД.
Чтобы не снимать типовую конфигурацию с поддержки и сохранить возможность беспрепятственного обновления, лучше всего использовать механизм Расширений конфигурации. Нам необходимо скорректировать условия проверки в цикле так, чтобы система корректно обрабатывала не только нулевые, но и любые отрицательные значения.
Для этого заменим строгое равенство нулю на оператор «меньше или равно» (<= 0). Выполним следующие действия по шагам:
ОперативныеВзаиморасчетыСервер.ЗаполнитьОперативныеВзаиморасчеты и перенесите её структуру в расширение, используя аннотацию &ИзменениеИКонтроль или &Вместо.
// Было в типовой конфигурации:
Если ПланыОплат[счПриход].КОплате = 0 Тогда
счПриход = счПриход + 1;
Продолжить;
КонецЕсли;
Если ПланыОплат[счРасход].Оплачено = 0 Тогда
счРасход = счРасход + 1;
Продолжить;
КонецЕсли;
// Стало в расширении (безопасный вариант):
Если ПланыОплат[счПриход].КОплате <= 0 Тогда
счПриход = счПриход + 1;
Продолжить;
КонецЕсли;
Если ПланыОплат[счРасход].Оплачено <= 0 Тогда
счРасход = счРасход + 1;
Продолжить;
КонецЕсли;
Аналогичные изменения необходимо внести во второй блок сопоставления (в конце цикла):
// Было:
Если ПланыОплат[счПриход].КОплате = 0 Тогда
счПриход = счПриход + 1;
счРасход = 0;
ИначеЕсли ПланыОплат[счРасход].Оплачено = 0 Тогда
счРасход = счРасход + 1;
КонецЕсли;
// Стало:
Если ПланыОплат[счПриход].КОплате <= 0 Тогда
счПриход = счПриход + 1;
счРасход = 0;
ИначеЕсли ПланыОплат[счРасход].Оплачено <= 0 Тогда
счРасход = счРасход + 1;
КонецЕсли;
После применения данного расширения бесконечный цикл прекратится. Программа просто пропустит некорректные строки с отрицательными остатками и успешно проведет документ.
Если вы принципиально не хотите вносить изменения в программный код системы, то проблему необходимо решать на уровне данных. Причина зависания — это следствие ошибок в регистрах. Найдем их с помощью Консоли запросов.
Напишем и выполним запрос к виртуальным таблицам остатков регистров планов оплат. Мы ищем записи с отрицательными значениями по ресурсам КОплате или Оплачено:
ВЫБРАТЬ
"Расчеты с поставщиками" КАК Источник,
ПланОплат.Регистратор КАК ДокументДвижения,
ПланОплат.ОбъектРасчетов КАК ОбъектРасчетов,
ПланОплат.КОплатеОстаток КАК СуммаКОплате,
ПланОплат.ОплаченоОстаток КАК СуммаОплачено
ИЗ
РегистрНакопления.РасчетыСПоставщикамиПланОплат.Остатки КАК ПланОплат
ГДЕ
ПланОплат.КОплатеОстаток < 0
ИЛИ ПланОплат.ОплаченоОстаток < 0
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
"Расчеты с клиентами",
ПланОплатКлиенты.Регистратор,
ПланОплатКлиенты.ОбъектРасчетов,
ПланОплатКлиенты.КОплатеОстаток,
ПланОплатКлиенты.ОплаченоОстаток
ИЗ
РегистрНакопления.РасчетыСКлиентамиПланОплат.Остатки КАК ПланОплатКлиенты
ГДЕ
ПланОплатКлиенты.КОплатеОстаток < 0
ИЛИ ПланОплатКлиенты.ОплаченоОстаток < 0
Что делать с обнаруженными строками?
ОбъектуРасчетов (это может быть договор, заказ или накладная).Корректировка задолженности или операцией ручной корректировки регистров (через документ Отражение расчетов с поставщиками/клиентами), чтобы обнулить некорректные отрицательные остатки в регистрах планирования оплат.Если списание денежных средств нужно провести срочно, а времени на поиск ошибок в регистрах или написание расширения нет, можно временно изменить режим выполнения расчетов в настройках программы:
Онлайн в режим Офлайн (отложенное распределение).Списание безналичных ДС. Документ проведется мгновенно без зависаний, так как ресурсоемкий расчет распределения оплат по графикам будет перенесен из транзакции проведения в регламентное фоновое задание.---
Зависание при проведении документов списания безналичных денежных средств в решениях 1С:ERP и КА 2 — это классический пример архитектурной уязвимости кода к некорректным входящим данным. Разработчики не предусмотрели появление отрицательных значений в регистрах планов оплат, что приводит к бесконечным циклам в процедуре ЗаполнитьОперативныеВзаиморасчеты общего модуля ОперативныеВзаиморасчетыСервер.
Для долгосрочного решения мы рекомендуем использовать Вариант 1 (написание расширения), так как оно защитит вашу базу от подобных сбоев в будущем при возникновении любых учетных ошибок пользователей. Для быстрого исправления текущей ситуации отлично подойдет Вариант 3 (переключение на офлайн-расчеты) с последующей очисткой отрицательных остатков с помощью Варианта 2.