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