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