При программной работе с документами в 1С:Предприятие разработчики часто сталкиваются с ситуацией, когда созданный из кода документ отказывается проводиться, выдавая системные ошибки СУБД. Одна из самых распространенных и при этом неочевидных проблем — ошибка «Деление на 0 значения типа Numeric» при вызове метода Записать(РежимЗаписиДокумента.Проведение) для документа ПоступлениеНаРасчетныйСчет. При этом, если открыть этот же документ интерактивно, перевыбрать в нем сумму или договор и нажать «Провести», проведение выполняется без каких-либо нареканий.
В этой статье мы подробно разберем, почему возникает эта ошибка, как локализовать ее в коде запросов проведения и как правильно заполнять реквизиты документа программно, чтобы раз и навсегда избежать подобных сбоев.
Давайте проанализируем, где именно происходит сбой. Как видно из стека ошибки, СУБД прерывает работу на строке выполнения пакетного запроса в модуле менеджера документа:
Результат = Запрос.ВыполнитьПакет();
Внутри функции ПодготовитьПараметрыПроведения формируется сложный пакетный запрос, собирающий данные из шапки и табличной части РасшифровкаПлатежа. В этих запросах рассчитываются рублевые эквиваленты сумм и показатели взаиморасчетов. Для этого платформа использует стандартные формулы пересчета валют. В обобщенном виде формула выглядит так:
СуммаВзаиморасчетов = СуммаПлатежа * КурсВзаиморасчетов / КратностьВзаиморасчетов
Когда мы создаем документ программно и добавляем новую строку в табличную часть РасшифровкаПлатежа, числовые реквизиты КурсВзаиморасчетов и КратностьВзаиморасчетов по умолчанию инициализируются нулями. Если мы забываем заполнить их в коде, SQL-сервер пытается выполнить деление на значение реквизита КратностьВзаиморасчетов, равное 0. На уровне базы данных это моментально вызывает исключение деления на ноль.
Многие начинающие разработчики задаются вопросом: почему при открытии формы и ручном изменении любого реквизита (например, суммы или договора) документ проводится нормально?
Дело в том, что в форме документа ПоступлениеНаРасчетныйСчет заложены интерактивные механизмы автозаполнения. При изменении суммы или договора на клиенте срабатывает обработчик события, который вызывает серверную процедуру СуммаДокументаПриИзмененииСервер(). Эта процедура обращается к общему модулю:
ПоступлениеНаРасчетныйСчетФормы.СуммаДокументаПриИзменении(ЭтотОбъект);
Этот алгоритм автоматически определяет валюту регламентированного учета и валюту расчетов по договору, после чего принудительно устанавливает дефолтные значения для незаполненных полей:
КурсВзаиморасчетов равным 1 (для рублевых расчетов) или актуальному курсу валюты на дату документа.КратностьВзаиморасчетов равной 1 (или кратности валюты из справочника).СуммуВзаиморасчетов, равную сумме платежа (если валюты совпадают).Когда мы записываем документ программно, код формы не выполняется, поэтому реквизиты табличной части так и остаются нулевыми, что ведет к ошибке проведения.
Для того чтобы убедиться, в каком именно месте запроса происходит деление на ноль, мы можем использовать стандартные инструменты отладки 1С. Разберем по шагам, как это сделать:
ПоступлениеНаРасчетныйСчет функцию ПодготовитьПараметрыПроведения.Результат = Запрос.ВыполнитьПакет();.Запрос.Текст./ и проанализируем переменные или поля, находящиеся в знаменателе.Чаще всего в тексте временных таблиц вы обнаружите конструкции вида:
ВЫБОР
КОГДА ТаблицаДокумента.КратностьВзаиморасчетов = 0
ТОГДА ...
ИНАЧЕ ТаблицаДокумента.СуммаПлатежа * ТаблицаДокумента.КурсВзаиморасчетов / ТаблицаДокумента.КратностьВзаиморасчетов
КОНЕЦ
Если разработчики конфигурации забыли поставить проверку на равенство знаменателя нулю в одной из временных таблиц пакета, СУБД выдаст ошибку.
Чтобы документ гарантированно проводился программно, мы должны полностью имитировать заполнение всех ключевых полей, которые на форме рассчитываются автоматически. Ниже приведен пример правильного программного создания документа с заполнением всех необходимых реквизитов взаиморасчетов:
// Создаем новый документ
ДокументОбъект = Документы.ПоступлениеНаРасчетныйСчет.СоздатьДокумент();
ДокументОбъект.Дата = ТекущаяДата();
ДокументОбъект.Организация = СсылкаНаОрганизацию;
ДокументОбъект.БанковскийСчет = БанковскийСчетОрганизации;
ДокументОбъект.Контрагент = СсылкаНаКонтрагента;
ДокументОбъект.СуммаДокумента = 150000;
ДокументОбъект.ВалютаДокумента = Справочники.Валюты.НайтиПоКоду("643"); // Рубли
// Указываем вид операции (например, ОплатаОтПокупателя)
ДокументОбъект.ВидОперации = Перечисления.ВидыОперацийПоступлениеДенежныхСредств.ОплатаПокупателя;
// Добавляем строку в расшифровку платежа
НоваяСтрока = ДокументОбъект.РасшифровкаПлатежа.Добавить();
НоваяСтрока.ДоговорКонтрагента = СсылкаНаДоговор;
НоваяСтрока.СуммаПлатежа = 150000;
// Защита от деления на 0 при проведении:
// Явно заполняем параметры курса и кратности взаиморасчетов
НоваяСтрока.КурсВзаиморасчетов = 1;
НоваяСтрока.КратностьВзаиморасчетов = 1;
НоваяСтрока.СуммаВзаиморасчетов = 150000;
// Заполняем счета учета и аналитику для бухгалтерского учета
НоваяСтрока.СтавкаНДС = Перечисления.СтавкиНДС.БезНДС;
НоваяСтрока.СпособПогашенияЗадолженности = Перечисления.СпособыПогашенияЗадолженности.Автоматически;
НоваяСтрока.СчетУчетаРасчетовСКонтрагентом = ПланыСчетов.Хозрасчетный.РасчетыСПокупателями; // 62.01
НоваяСтрока.СчетУчетаРасчетовПоАвансам = ПланыСчетов.Хозрасчетный.РасчетыПоАвансамПолученным; // 62.02
// Записываем и проводим документ
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Сообщить("Документ успешно проведен: " + ДокументОбъект.Ссылка);
Исключение
Сообщить("Ошибка при проведении документа: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
Аналогичная проблема деления на ноль может возникнуть, если вы создаете документ с другими видами операций. Например, при получении оплаты по платежным картам (эквайринг) система рассчитывает комиссию банка. Если в коде программного создания не заполнить параметры договора эквайринга или сумму услуг банка, расчетные алгоритмы проведения также могут попытаться выполнить деление на незаполненные (нулевые) значения ставок или сумм.
Поэтому при программном создании документов всегда руководствуйтесь важным правилом: заполняйте все реквизиты табличных частей, которые прямо или косвенно влияют на расчет стоимостных показателей документа. Перед написанием кода программного создания рекомендуется создать аналогичный документ вручную, открыть его через обработку «Поиск и замена значений» или стандартную обработку «Групповое изменение реквизитов» и детально изучить структуру заполнения всех полей его табличных частей.