При доработке типовых конфигураций на обычных формах, таких как Управление торговлей 10.3, часто возникает необходимость расширить стандартные отчеты или печатные формы данными из нетиповых (кастомных) регистров. Типичный пример — получение актуальной ставки НДС для контрагента из регистра сведений и выполнение математических расчетов на ее основе. В рамках данной статьи мы подробно разберем, как правильно составить запрос к регистру, обработать его результат и избежать распространенных ошибок при преобразовании типов данных.
Предположим, у нас есть регистр сведений КредитныеЛимиты, где в разрезе контрагентов хранится ресурс НДС (тип ПеречислениеСсылка.СтавкиНДС). Нам нужно вывести это значение в макет отчета и рассчитать «Лимит без НДС». Основная сложность для начинающих разработчиков заключается в том, что в базе данных ставка хранится как ссылка на объект перечисления, а для формул в макете требуется числовое значение.
Рассмотрим процесс реализации по шагам, начиная с создания функции получения данных.
Для получения актуальных данных на определенную дату в 1С используется виртуальная таблица СрезПоследних. Проанализируем правильную структуру функции, которая будет возвращать ставку НДС:
Функция ОпределитьНДС(Контрагент, ДатаСреза)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КредитныеЛимитыСрезПоследних.НДС КАК СтавкаНДС
|ИЗ
| РегистрСведений.КредитныеЛимиты.СрезПоследних(&ДатаСреза, Контрагент = &Контрагент) КАК КредитныеЛимитыСрезПоследних";
Запрос.УстановитьПараметр("ДатаСреза", КонецДня(ДатаСреза));
Запрос.УстановитьПараметр("Контрагент", Контрагент);
РезультатЗапроса = Запрос.Выполнить();
// Используем выборку для получения значения
Выборка = РезультатЗапроса.Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.СтавкаНДС;
Иначе
Возврат Перечисления.СтавкиНДС.ПустаяСсылка();
КонецЕсли;
КонецФункции
Важно обратить внимание на метод Выбрать() и последующий вызов Следующий(). В отличие от метода Выгрузить(), который создает таблицу значений в памяти, Выбрать() работает быстрее и эффективнее, если нам нужно получить всего одну строку данных. Если Следующий() возвращает Ложь, это означает, что записей по данному контрагенту в регистре нет, и мы возвращаем пустую ссылку.
Поскольку ресурс НДС имеет ссылочный тип, мы не можем напрямую умножить его на сумму лимита. Проанализируем ситуацию: в 1С ПеречислениеСсылка.СтавкиНДС — это объект, а нам нужно число (10, 18 или 20). Для этого напишем вспомогательную функцию преобразования, которую можно подсмотреть в типовых механизмах (например, в 1С:ERP или УПП):
Функция ПолучитьЧисловуюСтавкуНДС(СтавкаНДС)
Если СтавкаНДС = Перечисления.СтавкиНДС.НДС20
Или СтавкаНДС = Перечисления.СтавкиНДС.НДС20_120 Тогда
Возврат 20;
ИначеЕсли СтавкаНДС = Перечисления.СтавкиНДС.НДС18
Или СтавкаНДС = Перечисления.СтавкиНДС.НДС18_118 Тогда
Возврат 18;
ИначеЕсли СтавкаНДС = Перечисления.СтавкиНДС.НДС10
Или СтавкаНДС = Перечисления.СтавкиНДС.НДС10_110 Тогда
Возврат 10;
Иначе
Возврат 0;
КонецЕсли;
КонецФункции
Использование такой функции позволяет избежать ошибки «Преобразование значения к типу Число не может быть выполнено», которая возникает при попытке использовать ссылку в арифметических операциях.
Теперь объединим все части в основном коде формирования отчета. Предположим, мы обходим список контрагентов в цикле и выводим секцию макета. Выясним, как правильно передать параметры в область макета:
// В цикле по строкам отчета
КонтрагентОбъект = СтрокаДанных.Контрагент;
Лимит = ОпределитьЛимит(КонтрагентОбъект); // Получаем сумму лимита (число)
// 1. Получаем ссылку на НДС
СсылкаНДС = ОпределитьНДС(КонтрагентОбъект, ДатаОтчета);
// 2. Получаем числовое значение для расчетов
ЧислоНДС = ПолучитьЧисловуюСтавкуНДС(СсылкаНДС);
// 3. Выполняем расчет "Лимит без НДС"
// Правильная формула для НДС "в том числе": Сумма / (1 + Ставка/100)
Если ЧислоНДС > 0 Тогда
ЛимитБезНДС = Лимит / (1 + ЧислоНДС / 100);
Иначе
ЛимитБезНДС = Лимит;
КонецЕсли;
// 4. Заполнение параметров секции
Секция = Макет.ПолучитьОбласть("Строка");
Секция.Параметры.Контрагент = КонтрагентОбъект;
Секция.Параметры.Лимит = Лимит;
Секция.Параметры.СтавкаНДС = СсылкаНДС; // В макет выведется строковое представление, например "20%"
Секция.Параметры.ЛимитБезНДС = ЛимитБезНДС;
ТабДок.Вывести(Секция);
Рассмотрим важный нюанс. В примере выше функция ОпределитьНДС вызывается внутри цикла. Если в отчете 1000 контрагентов, программа сделает 1000 запросов к базе данных, что значительно замедлит работу. Проанализируем, как сделать это правильно («по-программистски»):
ГДЕ Контрагент В (&МассивКонтрагентов).Контрагент, а значением — СтавкаНДС.Ставка = СоответствиеНДС.Получить(ТекущийКонтрагент).Это позволит сократить время формирования отчета в десятки раз.
Часто в старых записях или у новых контрагентов данные в регистре могут отсутствовать. Чтобы отчет не «падал» с ошибками, всегда проверяйте значения. В запросе можно использовать функцию ЕСТЬNULL, а в коде 1С — проверку ЗначениеЗаполнено().
Например, в тексте запроса можно сразу возвращать пустую ссылку вместо NULL:
ЕСТЬNULL(КредитныеЛимитыСрезПоследних.НДС, ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.ПустаяСсылка))
Такой подход гарантирует стабильную работу системы даже при неполном заполнении справочников и регистров.