Как исправить ошибку «Деление на 0» при программном проведении документа «Поступление на расчетный счет»?

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

При программной работе с документами в 1С:Предприятие разработчики часто сталкиваются с ситуацией, когда созданный из кода документ отказывается проводиться, выдавая системные ошибки СУБД. Одна из самых распространенных и при этом неочевидных проблем — ошибка «Деление на 0 значения типа Numeric» при вызове метода Записать(РежимЗаписиДокумента.Проведение) для документа ПоступлениеНаРасчетныйСчет. При этом, если открыть этот же документ интерактивно, перевыбрать в нем сумму или договор и нажать «Провести», проведение выполняется без каких-либо нареканий.

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

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

Давайте проанализируем, где именно происходит сбой. Как видно из стека ошибки, СУБД прерывает работу на строке выполнения пакетного запроса в модуле менеджера документа:

Результат = Запрос.ВыполнитьПакет();

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

СуммаВзаиморасчетов = СуммаПлатежа * КурсВзаиморасчетов / КратностьВзаиморасчетов

Когда мы создаем документ программно и добавляем новую строку в табличную часть РасшифровкаПлатежа, числовые реквизиты КурсВзаиморасчетов и КратностьВзаиморасчетов по умолчанию инициализируются нулями. Если мы забываем заполнить их в коде, SQL-сервер пытается выполнить деление на значение реквизита КратностьВзаиморасчетов, равное 0. На уровне базы данных это моментально вызывает исключение деления на ноль.

Почему ручное изменение на форме решает проблему?

Многие начинающие разработчики задаются вопросом: почему при открытии формы и ручном изменении любого реквизита (например, суммы или договора) документ проводится нормально?

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

ПоступлениеНаРасчетныйСчетФормы.СуммаДокументаПриИзменении(ЭтотОбъект);

Этот алгоритм автоматически определяет валюту регламентированного учета и валюту расчетов по договору, после чего принудительно устанавливает дефолтные значения для незаполненных полей:

  1. Устанавливает КурсВзаиморасчетов равным 1 (для рублевых расчетов) или актуальному курсу валюты на дату документа.
  2. Устанавливает КратностьВзаиморасчетов равной 1 (или кратности валюты из справочника).
  3. Заполняет СуммуВзаиморасчетов, равную сумме платежа (если валюты совпадают).

Когда мы записываем документ программно, код формы не выполняется, поэтому реквизиты табличной части так и остаются нулевыми, что ведет к ошибке проведения.

Как отловить ошибку с помощью отладчика?

Для того чтобы убедиться, в каком именно месте запроса происходит деление на ноль, мы можем использовать стандартные инструменты отладки 1С. Разберем по шагам, как это сделать:

  1. Найдем в модуле менеджера документа ПоступлениеНаРасчетныйСчет функцию ПодготовитьПараметрыПроведения.
  2. Установим точку останова на строке Результат = Запрос.ВыполнитьПакет();.
  3. Запустим программное создание документа в режиме отладки.
  4. При остановке системы вызовем окно «Табло» (Shift + F9) или «Вычислить выражение» и проанализируем свойство Запрос.Текст.
  5. Скопируем текст запроса, найдем все символы косой черты / и проанализируем переменные или поля, находящиеся в знаменателе.

Чаще всего в тексте временных таблиц вы обнаружите конструкции вида:


ВЫБОР
    КОГДА ТаблицаДокумента.КратностьВзаиморасчетов = 0
        ТОГДА ...
    ИНАЧЕ ТаблицаДокумента.СуммаПлатежа * ТаблицаДокумента.КурсВзаиморасчетов / ТаблицаДокумента.КратностьВзаиморасчетов
КОНЕЦ

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

Правильное программное создание и заполнение документа

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


// Создаем новый документ
ДокументОбъект = Документы.ПоступлениеНаРасчетныйСчет.СоздатьДокумент();
ДокументОбъект.Дата = ТекущаяДата();
ДокументОбъект.Организация = СсылкаНаОрганизацию;
ДокументОбъект.БанковскийСчет = БанковскийСчетОрганизации;
ДокументОбъект.Контрагент = СсылкаНаКонтрагента;
ДокументОбъект.СуммаДокумента = 150000;
ДокументОбъект.ВалютаДокумента = Справочники.Валюты.НайтиПоКоду("643"); // Рубли

// Указываем вид операции (например, ОплатаОтПокупателя)
ДокументОбъект.ВидОперации = Перечисления.ВидыОперацийПоступлениеДенежныхСредств.ОплатаПокупателя;

// Добавляем строку в расшифровку платежа
НоваяСтрока = ДокументОбъект.РасшифровкаПлатежа.Добавить();
НоваяСтрока.ДоговорКонтрагента = СсылкаНаДоговор;
НоваяСтрока.СуммаПлатежа = 150000;

// Защита от деления на 0 при проведении:
// Явно заполняем параметры курса и кратности взаиморасчетов
НоваяСтрока.КурсВзаиморасчетов = 1; 
НоваяСтрока.КратностьВзаиморасчетов = 1; 
НоваяСтрока.СуммаВзаиморасчетов = 150000; 

// Заполняем счета учета и аналитику для бухгалтерского учета
НоваяСтрока.СтавкаНДС = Перечисления.СтавкиНДС.БезНДС;
НоваяСтрока.СпособПогашенияЗадолженности = Перечисления.СпособыПогашенияЗадолженности.Автоматически;
НоваяСтрока.СчетУчетаРасчетовСКонтрагентом = ПланыСчетов.Хозрасчетный.РасчетыСПокупателями; // 62.01
НоваяСтрока.СчетУчетаРасчетовПоАвансам = ПланыСчетов.Хозрасчетный.РасчетыПоАвансамПолученным; // 62.02

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

Важные нюансы при работе с другими операциями (Эквайринг, Комиссия)

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

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

← На главную