ТабличныйДокумент в управляемой форме: очистка, удаление строк и перезаполнение без ошибок

· 9 мин

Содержание

Табличный документ в 1С 8.3 очищают одной строкой: ТабДок.Очистить(). В тонком клиенте этот вызов работает и в процедуре &НаКлиенте, и в &НаСервере, если ТабДок — реквизит формы типа ТабличныйДокумент. А вот заполнить документ заново из макета на клиенте нельзя: макеты доступны только на сервере, поэтому очистку и вывод обычно делают в одной серверной процедуре.

Типичная боль выглядит так: кнопка «Сформировать» нажата второй раз, а новый отчёт дописался под старым. Или код из обычного приложения перенесли в управляемую форму, и он перестал компилироваться. Оба случая решаются правильным разделением очистки и вывода между клиентом и сервером, плюс парой приёмов для удаления строк, колонок и именованных областей.

Как устроен табличный документ в тонком клиенте

В тонком клиенте форма живёт на двух сторонах. Данные формы, включая реквизит типа ТабличныйДокумент, передаются между клиентом и сервером при каждом серверном вызове с контекстом. На экране документ показывает элемент формы — поле табличного документа.

Из этого следуют три практических правила:

  • 📄 Реквизит и элемент — разные вещи. Очищают реквизит (ТабДок), а не элемент (Элементы.ТабДок). У элемента нет метода Очистить().
  • 🖥️ Макеты — только на сервере. ПолучитьМакет() у объекта или менеджера и ПолучитьОбщийМакет() в тонком клиенте недоступны. Отсюда и большинство ошибок при переносе старого кода.
  • 🔁 Каждый вызов &НаСервере гоняет документ туда и обратно. Если в документе десятки тысяч строк, это заметно по скорости.

Сам объект ТабличныйДокумент на клиенте доступен: можно читать ячейки, удалять строки, очищать. Нельзя только достать макет конфигурации.

ДействиеНа клиентеНа сервере
ТабДок.Очистить()дада
ТабДок.УдалитьОбласть(...)дада
Чтение ячеек через Область(...)дада
ПолучитьМакет(), ПолучитьОбщийМакет()нетда
Запрос к базе для заполнениянетда

Метод Очистить(): базовая очистка содержимого

Самый частый сценарий — перед повторным формированием отчёта стереть всё, что было выведено раньше. Делают это первой строкой серверной процедуры заполнения:

&НаКлиенте
Процедура Сформировать(Команда)
    СформироватьНаСервере();
КонецПроцедуры

&НаСервере
Процедура СформироватьНаСервере()

    ТабДок.Очистить();

    Макет = РеквизитФормыВЗначение("Объект").ПолучитьМакет("Макет");
    ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
    ОбластьСтрока = Макет.ПолучитьОбласть("Строка");

    ТабДок.Вывести(ОбластьШапка);

    Запрос = Новый Запрос("ВЫБРАТЬ Наименование ИЗ Справочник.Номенклатура");
    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл
        ОбластьСтрока.Параметры.Наименование = Выборка.Наименование;
        ТабДок.Вывести(ОбластьСтрока);
    КонецЦикла;

КонецПроцедуры

Пример для обработки или отчёта с основным реквизитом Объект. В форме документа или справочника вместо РеквизитФормыВЗначение("Объект") вызывают модуль менеджера: Справочники.Номенклатура.ПолучитьМакет("Макет").

Если нужно просто стереть документ без перезаполнения, например по кнопке «Очистить», сервер не нужен:

&НаКлиенте
Процедура ОчиститьРезультат(Команда)
    ТабДок.Очистить();
КонецПроцедуры

Такой вызов не тратит серверный вызов и срабатывает мгновенно.

Очистить() или Новый ТабличныйДокумент

Иногда вместо очистки пишут ТабДок = Новый ТабличныйДокумент. Для реквизита формы так лучше не делать: новый объект приходит с настройками по умолчанию. Всё, что задано для документа в коде формы раньше (параметры печати, фиксация шапки, ключ параметров печати), придётся выставлять заново.

💡 Для локальной переменной внутри процедуры разницы нет: создайте новый документ, заполните его и только в конце присвойте реквизиту или верните из функции.

Параметры печати (ОриентацияСтраницы, поля, масштаб) и фиксацию строк надёжнее задавать после заполнения, в конце процедуры. Тогда документ выглядит одинаково при первом и повторном формировании.

Удаление строк и колонок из табличного документа программно

Когда нужно убрать только часть содержимого, применяют УдалитьОбласть(). Метод принимает область и тип смещения: что делать с ячейками, стоящими ниже или правее.

Удалить строки

// удалить строки с 5-й по 10-ю, нижние строки поднимутся вверх
Область = ТабДок.Область("R5:R10");
ТабДок.УдалитьОбласть(Область, ТипСмещенияТабличногоДокумента.ПоВертикали);

Удалить колонки

// удалить 3-ю и 4-ю колонки, правые сдвинутся влево
Область = ТабДок.Область("C3:C4");
ТабДок.УдалитьОбласть(Область, ТипСмещенияТабличногоДокумента.ПоГоризонтали);

Удалить всё ниже заданной строки

Бывает, что шапку отчёта на первых строках нужно сохранить, а всё, что под ней, убрать. Номер последней строки берите из свойства ВысотаТаблицы, а не задавайте числом наугад:

ПерваяУдаляемая = 6;
Если ТабДок.ВысотаТаблицы >= ПерваяУдаляемая Тогда
    Адрес = "R" + Формат(ПерваяУдаляемая, "ЧГ=0") + ":R" + Формат(ТабДок.ВысотаТаблицы, "ЧГ=0");
    ТабДок.УдалитьОбласть(ТабДок.Область(Адрес), ТипСмещенияТабличногоДокумента.ПоВертикали);
КонецЕсли;

Проверка высоты защищает от ошибки на пустом документе: если строк меньше шести, удалять нечего и адрес области получился бы неверным.

Удалить строки по условию

Частая задача — убрать из готового документа пустые строки или строки с нулевой суммой. Удалять нужно снизу вверх, иначе после каждого удаления номера оставшихся строк съезжают и цикл пропускает соседние строки.

&НаКлиенте
Процедура УдалитьПустыеСтроки(Команда)

    Высота = ТабДок.ВысотаТаблицы;
    Для Сч = 1 По Высота Цикл
        НомерСтроки = Высота - Сч + 1;
        Ячейка = ТабДок.Область(НомерСтроки, 2, НомерСтроки, 2);
        Если ПустаяСтрока(Ячейка.Текст) Тогда
            ТабДок.УдалитьОбласть(
                ТабДок.Область("R" + Формат(НомерСтроки, "ЧГ=0") + ":R" + Формат(НомерСтроки, "ЧГ=0")),
                ТипСмещенияТабличногоДокумента.ПоВертикали);
        КонецЕсли;
    КонецЦикла;

КонецПроцедуры

Обратите внимание на Формат(..., "ЧГ=0"): без него номер строки 1250 превратится в строку с неразрывным пробелом, и адрес R1 250 окажется неверным.

Стереть текст, но оставить строки

Если строки должны остаться на месте, а пропасть должно только содержимое, используйте метод Очистить() у самой области ячеек. У него три необязательных параметра: очищать ли текст, формат и примечания.

Область = ТабДок.Область("R5C1:R10C6");
Область.Очистить(Истина, Ложь, Ложь); // только текст, оформление сохранится

Именованные области и макеты при перезаполнении

Именованная область — участок макета или документа со своим именем: «Шапка», «Строка», «Итоги». В готовом документе такие имена сохраняются, если их задали при выводе или в макете.

Найти и удалить именованную область:

Итоги = ТабДок.Области.Найти("Итоги");
Если Итоги <> Неопределено Тогда
    ТабДок.УдалитьОбласть(Итоги, ТипСмещенияТабличногоДокумента.ПоВертикали);
КонецЕсли;

Проверка на Неопределено обязательна. Обращение ТабДок.Области.Итоги к несуществующей области падает с ошибкой.

Частый приём — частично обновить отчёт: удалить табличную часть, а шапку оставить. Порядок такой:

  1. Найдите номер первой строки табличной части, например по ВерхняяГраница у именованной области.
  2. Удалите строки от этой границы до ТабДок.ВысотаТаблицы.
  3. На сервере выведите новые строки из макета через ТабДок.Вывести(...) — они добавятся в конец, то есть сразу под шапку.

💡 Получайте области макета через ПолучитьОбласть() один раз, до цикла. В цикле меняйте только Параметры и вызывайте Вывести(). Параметры области не сбрасываются сами: если в строке поле может быть пустым, явно присваивайте ему пустое значение, иначе в строку попадёт значение из предыдущей итерации.

Очистка при выводе нескольких документов в один

Печатная форма часто собирается сразу для нескольких выделенных документов: пять накладных подряд в одном табличном документе. Здесь очистка нужна ровно один раз — до начала цикла, а не для каждого документа.

Типичная схема на сервере:

  1. Очистите итоговый документ: ТабДок.Очистить().
  2. В цикле по ссылкам выведите шапку, строки и подвал очередного документа.
  3. Перед каждым следующим документом, кроме первого, вызовите ТабДок.ВывестиГоризонтальныйРазделительСтраниц(), чтобы он начинался с новой страницы.
  4. После цикла задайте параметры печати и ключ КлючПараметровПечати, чтобы платформа запоминала настройки принтера для этой формы.
&НаСервере
Процедура ВывестиНесколько(МассивСсылок)

    ТабДок.Очистить();
    ПервыйДокумент = Истина;

    Для Каждого Ссылка Из МассивСсылок Цикл
        Если Не ПервыйДокумент Тогда
            ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
        КонецЕсли;
        ПервыйДокумент = Ложь;
        // ... вывод областей для Ссылка ...
    КонецЦикла;

    ТабДок.ОриентацияСтраницы = ОриентацияСтраницы.Портрет;
    ТабДок.КлючПараметровПечати = "ПечатьНакладной";

КонецПроцедуры

Если очистку поставить внутрь цикла, на экране останется только последний документ — частая ошибка при переделке печати одного документа в пакетную.

💡 Хотите, чтобы табличная часть не разрывалась посреди строки при переходе на новую страницу? Перед выводом очередной строки проверьте, помещается ли она, методом ТабДок.ПроверитьВывод(...) с массивом выводимых областей. Если не помещается — выведите разделитель страниц и повторите шапку таблицы.

Скорость: большие отчёты в тонком клиенте

На маленьком документе разница между способами незаметна. На отчёте в 20–50 тысяч строк она измеряется секундами. Что реально помогает:

  • ⚡ Одна очистка вместо цикла удалений. Если нужно убрать почти всё, быстрее Очистить() и вывести заново нужное, чем тысячу раз вызывать УдалитьОбласть().
  • 📦 Один серверный вызов. Очистка, запрос и вывод — в одной процедуре &НаСервере. Не вызывайте сервер в цикле по строкам с клиента.
  • 🧩 Сборка в локальной переменной. Функция &НаСервереБезКонтекста строит документ в переменной и возвращает его. На сервер не уходят остальные данные формы, только результат приходит обратно.
  • ⏳ Фоновое задание для долгих отчётов. Если формирование занимает больше нескольких секунд, выполняйте его в фоне и кладите результат во временное хранилище через ПоместитьВоВременноеХранилище(). Форма при этом не зависает.

Пример сборки без контекста формы:

&НаКлиенте
Процедура Сформировать(Команда)
    ТабДок = СформироватьДокумент(Период);
КонецПроцедуры

&НаСервереБезКонтекста
Функция СформироватьДокумент(Период)
    Результат = Новый ТабличныйДокумент;
    // ... запрос и вывод областей в Результат ...
    Возврат Результат;
КонецФункции

Здесь очищать нечего: присваивание реквизиту полностью заменяет старый документ.

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

Показать, что отчёт устарел

Пока идёт долгое формирование, пользователь видит старые цифры и может принять их за актуальные. В управляемой форме у поля табличного документа есть свойство ОтображениеСостояния. Через него поверх поля выводят полупрозрачную пелену и текст вроде «Отчёт не сформирован», а после заполнения убирают её. Так сделано в отчётах типовых конфигураций на БСП.

&НаКлиенте
Процедура ПоказатьНеактуальность()
    Состояние = Элементы.ТабДок.ОтображениеСостояния;
    Состояние.Видимость = Истина;
    Состояние.ДополнительныйРежимОтображения = ДополнительныйРежимОтображения.Неактуальность;
    Состояние.Текст = "Отчёт не сформирован. Нажмите «Сформировать».";
КонецПроцедуры

После вывода нового результата выставьте Видимость = Ложь и ДополнительныйРежимОтображения.НеИспользовать.

Частые ошибки при очистке и перезаполнении

Диагностика: выберите симптом

Ещё одна неочевидная ситуация — вызов Очистить() у элемента формы: Элементы.ТабДок.Очистить(). Такая строка не скомпилируется, потому что у поля табличного документа нет этого метода. Очищайте реквизит, к которому поле привязано, — его имя видно в свойстве поля ПутьКДанным.

Если пользователю нужно очистить документ вручную

Не всегда очистку пишет программист. Если в поле табличного документа включено редактирование (свойство Редактирование у поля формы), пользователь может выделить ячейки мышью и нажать Delete — текст в выделенных ячейках сотрётся.

Когда это разрешать, а когда нет:

  • ✅ Форма-«черновик», где пользователь сам готовит таблицу для печати или выгрузки.
  • ❌ Отчёт с расшифровками: ручная правка сбивает связь цифр с данными, а при следующем формировании изменения всё равно пропадут.

Для отчётов надёжнее отдельная команда «Очистить» в командной панели формы, которая вызывает ТабДок.Очистить() на клиенте, и запрет редактирования поля.

Частые вопросы

Сбрасывает ли Очистить() расшифровки ячеек?
Да. Вместе с содержимым удаляются и значения расшифровки, поэтому после перезаполнения их нужно выводить заново вместе с областями.
Можно ли очистить табличный документ в веб-клиенте так же, как в тонком?
Да. Код с &НаКлиенте и &НаСервере одинаково работает в тонком и веб-клиенте: объект ТабличныйДокумент доступен на клиенте в обоих режимах.
Как узнать, что документ уже пустой?
Проверьте ТабДок.ВысотаТаблицы = 0. У только что очищенного документа высота и ширина таблицы равны нулю.
Почему после очистки поле на форме прокручено не в начало?
Поле запоминает положение. После заполнения установите текущую область на первую ячейку: Элементы.ТабДок.ТекущаяОбласть = ТабДок.Область("R1C1").
Нужен ли монопольный режим или особые права, чтобы очистить документ?
Нет. Табличный документ в форме — это данные в памяти, а не таблица базы. Очистка не затрагивает информационную базу и не требует особых прав.