Новая строка в табличной части: от метода Добавить() до загрузки тысяч позиций

· 9 мин

Содержание

Чтобы добавить запись в табличную часть 1С из кода, вызовите Объект.ИмяТабличнойЧасти.Добавить(): метод вернёт новую пустую строку, в которую вы присваиваете значения реквизитов, а затем записываете объект. Пользователь в форме делает то же самое кнопкой Добавить над таблицей или клавишей Ins.

Способ выбирают по задаче. Одна-две строки по кнопке — Добавить() прямо в клиентской процедуре формы. Строка в определённом месте — Вставить(Индекс). Сотни и тысячи позиций — запрос, таблица значений и один вызов Загрузить(). Всё ниже относится к платформе 8.3 и управляемым формам, синтаксис одинаков для любой конфигурации: Бухгалтерии 3.0, ЗУП 3.1, УТ 11, ERP и самописных баз.

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

Табличная часть — это вложенная таблица внутри документа или справочника: «Товары» в реализации, «Начисления» в документе начисления зарплаты. У каждой строки есть служебный реквизит НомерСтроки, он заполняется платформой сам. Остальные колонки — реквизиты, которые разработчик описал в Конфигураторе.

Строки живут только вместе с объектом. Отдельно записать строку нельзя: вы меняете коллекцию и записываете весь документ целиком.

МетодЧто делаетКогда нужен
Добавить()Новая строка в конецОсновной случай
Вставить(Индекс)Новая строка на позицию, индекс с нуляСтрока-заголовок, вставка перед выбранной
Загрузить(ТаблицаЗначений)Заменяет всё содержимое строками таблицыМассовое заполнение
Сдвинуть(Строка, Смещение)Перемещает строку вверх или внизРучная сортировка
Очистить()Удаляет все строкиПерезаполнение с нуля

Минимальный пример на сервере, когда у вас есть ссылка на документ:

ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();

НоваяСтрока = ДокументОбъект.Товары.Добавить();
НоваяСтрока.Номенклатура = Номенклатура;
НоваяСтрока.Количество   = 5;
НоваяСтрока.Цена         = 120;
НоваяСтрока.Сумма        = НоваяСтрока.Количество * НоваяСтрока.Цена; // 600

ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);

Ключевой шаг — ПолучитьОбъект(). Через ссылку табличная часть доступна только на чтение, добавить в неё строку нельзя.

Если документ уже проведён, записывайте его в режиме РежимЗаписиДокумента.Проведение. Иначе табличная часть изменится, а движения по регистрам останутся старыми.

Вставка на конкретное место выглядит так. Индексы считаются с нуля, поэтому Вставить(0) даёт первую строку:

ПерваяСтрока = ДокументОбъект.Товары.Вставить(0);
ПерваяСтрока.Содержание = "Доставка";

Добавление строк в форме: что делает пользователь

У пользователя три штатных пути добавить запись в табличную часть:

  • ➕ кнопка Добавить в командной панели над таблицей или клавиша Ins — пустая строка в режиме редактирования;
  • 📄 копирование текущей строки клавишей F9 — новая строка с теми же значениями, удобно для позиций с одной ценой;
  • 🔍 кнопка Подбор в типовых документах — окно выбора номенклатуры, откуда переносится сразу много позиций.

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

Строка не уйдёт в базу, пока пользователь не нажмёт Записать или Провести. До этого все изменения хранятся в данных формы.

Программное добавление записи в табличную часть управляемой формы

В управляемой форме Объект — это не документ, а его копия в данных формы. Табличная часть там имеет тип ДанныеФормыКоллекция. Метод Добавить() у неё работает и на клиенте, и на сервере.

Добавить строку по своей кнопке

Процедура на клиенте: строка добавляется, курсор ставится на неё, форма помечается изменённой.

&НаКлиенте
Процедура ДобавитьУслугу(Команда)

	НоваяСтрока = Объект.Услуги.Добавить();
	НоваяСтрока.Количество = 1;

	Элементы.Услуги.ТекущаяСтрока = НоваяСтрока.ПолучитьИдентификатор();
	Модифицированность = Истина;

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

ТекущаяСтрока у таблицы формы принимает не саму строку, а её идентификатор. Поэтому нужен ПолучитьИдентификатор().

Если нужно, чтобы пользователь сразу начал вводить значения в новой строке, вызовите у элемента формы Элементы.Услуги.ДобавитьСтроку(). Это то же самое, что нажать Ins: сработают все интерактивные события.

Подставить значения по умолчанию при интерактивном добавлении

Для этого есть событие таблицы формы ПриНачалеРедактирования. Параметр НоваяСтрока отличает новую строку от редактирования старой, Копирование — копию от чистой строки.

&НаКлиенте
Процедура ТоварыПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)

	Если НоваяСтрока И Не Копирование Тогда
		Элемент.ТекущиеДанные.Количество = 1;
		Элемент.ТекущиеДанные.Склад = Объект.Склад;
	КонецЕсли;

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

Запретить добавление по условию можно в событии ПередНачаломДобавления: установите там Отказ = Истина.

Когда нужен сервер

На клиенте недоступны запросы и обращение к реквизитам ссылок через точку. Цену, ставку НДС, остаток получают в серверной процедуре. Если серверу контекст формы не нужен, используйте &НаСервереБезКонтекста: так на сервер не уходят данные всей формы.

Если серверный код работает с методами модуля объекта (например, процедурой заполнения), получите сам объект:

&НаСервере
Процедура ЗаполнитьПоОснованию()

	ДокОбъект = РеквизитФормыВЗначение("Объект");
	ДокОбъект.ЗаполнитьТоварыПоОснованию(); // ваша экспортная процедура модуля объекта
	ЗначениеВРеквизитФормы(ДокОбъект, "Объект");

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

После ЗначениеВРеквизитФормы строки табличной части получают новые идентификаторы. Сохранённые раньше значения ТекущаяСтрока становятся недействительными — получайте их заново.

Заполнение обязательных реквизитов строки и типы значений

Пустая строка после Добавить() содержит пустые значения своих типов: 0 для чисел, пустую строку, пустую ссылку, Неопределено для составного типа. Сумма сама не посчитается, ставка НДС не подставится.

Что проверить у каждой колонки

  • 🔢 Число — длина и точность из Конфигуратора. Если у реквизита 2 знака после запятой, а вы присваиваете результат деления, округлите явно: Окр(Сумма / Количество, 2).
  • 🔤 Строка фиксированной длины — значение длиннее лимита обрежется без предупреждения. Для комментариев с длинным текстом проверьте длину заранее через СтрДлина().
  • 🔗 Ссылка — присваивайте значение именно того типа, что указан. Справочник «Контрагенты» в поле «Номенклатура» не встанет.
  • 🧩 Составной тип — допустим любой из перечисленных типов. Если код ниже по тексту ждёт конкретный, проверяйте ТипЗнч(Значение) = Тип("СправочникСсылка.Номенклатура").

Не дублируйте логику пересчёта

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

Для перенесения значений из другой коллекции с теми же именами колонок есть ЗаполнитьЗначенияСвойств(Приёмник, Источник):

Для Каждого СтрокаИсточника Из ТаблицаИсточник Цикл
	НоваяСтрока = Объект.Товары.Добавить();
	ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаИсточника);
КонецЦикла;

Третьим параметром можно перечислить через запятую только нужные свойства, четвёртым — исключить лишние.

Массовая загрузка тысяч строк без тормозов

Сам вызов Добавить() быстрый. Медленно становится из-за того, что делают вокруг него.

Три приёма, которые дают основной выигрыш

  1. Один запрос вместо обращений через точку. Строка Номенклатура.ЕдиницаИзмерения внутри цикла читает элемент справочника из базы. На 5 000 строк это 5 000 обращений. Получите все нужные поля одним запросом.
  2. Загрузить() вместо цикла. Если данные уже лежат в таблице значений с теми же именами колонок, одной командой замените ими содержимое табличной части.
  3. Один вызов сервера. Не добавляйте строки на клиенте с отдельным серверным вызовом за ценой каждой строки. Передайте на сервер весь список и заполните табличную часть там.

Пример заполнения из запроса. Имя регистра в примере условное — подставьте своё:

&НаСервере
Процедура ЗаполнитьОстатками()

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Остатки.Номенклатура КАК Номенклатура,
	|	Остатки.КоличествоОстаток КАК Количество
	|ИЗ
	|	РегистрНакопления.ТоварыНаСкладах.Остатки(, Склад = &Склад) КАК Остатки";
	Запрос.УстановитьПараметр("Склад", Объект.Склад);

	Объект.Товары.Загрузить(Запрос.Выполнить().Выгрузить());

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

Колонки сопоставляются по имени. Поэтому в запросе важны псевдонимы КАК Количество: без них колонка не попадёт в нужный реквизит. Колонки, которых в табличной части нет, игнорируются. Реквизиты, которых нет в таблице, останутся пустыми.

Загрузить() удаляет все строки, которые были в табличной части до вызова. Если нужно дописать, а не заменить, сначала выгрузите текущие строки через Выгрузить(), добавьте новые в таблицу значений и загрузите результат обратно.

Схлопнуть повторы перед загрузкой

Файл от поставщика часто содержит одну позицию несколькими строками. Сверните таблицу перед загрузкой:

Таблица.Свернуть("Номенклатура, Цена", "Количество, Сумма");
Объект.Товары.Загрузить(Таблица);

Для поиска по большой таблице в цикле вместо перебора используйте Соответствие с ключом-ссылкой или индекс таблицы значений Таблица.Индексы.Добавить("Номенклатура").

Проверка строк перед записью и обработка ошибок

Платформа сама проверит реквизиты, у которых в Конфигураторе свойство «Проверка заполнения» установлено в «Выдавать ошибку». Для своих правил есть обработчик ОбработкаПроверкиЗаполнения в модуле объекта.

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

Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)

	Для Каждого Строка Из Товары Цикл
		Если Строка.Количество <= 0 Тогда
			Сообщение = Новый СообщениеПользователю;
			Сообщение.Текст = "Строка " + Формат(Строка.НомерСтроки, "ЧГ=0")
				+ ": количество должно быть больше нуля";
			Сообщение.Поле = "Товары[" + Формат(Строка.НомерСтроки - 1, "ЧГ=0") + "].Количество";
			Сообщение.УстановитьДанные(ЭтотОбъект);
			Сообщение.Сообщить();
			Отказ = Истина;
		КонецЕсли;
	КонецЦикла;

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

В поле сообщения индекс строки начинается с нуля, а НомерСтроки — с единицы. Отсюда вычитание единицы.

ОбработкаПроверкиЗаполнения вызывается при интерактивной записи и при явном вызове ПроверитьЗаполнение(). Документ, который код записывает в режиме проведения, тоже проходит проверку. А вот обычная запись из кода через Записать() без проведения её не запускает. Если строки добавляет обработка, вызовите ДокументОбъект.ПроверитьЗаполнение() перед записью.

При загрузке из внешних источников оборачивайте запись в Попытка … Исключение и пишите текст ошибки в журнал регистрации через ЗаписьЖурналаРегистрации(). Одна битая позиция не должна молча обрывать загрузку всего файла.

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

Особенности управляемых форм, о которых забывают

Управляемая форма делит код на клиент и сервер, и это влияет на добавление строк сильнее, чем кажется.

  • 🧭 Текущая строка. На клиенте данные выделенной строки берут через Элементы.Товары.ТекущиеДанные. Если строки не выделено, там Неопределено — проверяйте перед обращением.
  • 🔒 Только просмотр. Если у таблицы формы стоит свойство «Только просмотр», пользователь не добавит строку кнопкой, но код по-прежнему может. Для запрета на всех уровнях проверяйте условие в ПередЗаписью объекта.
  • 🧱 Расширение вместо правки типовой. Свою кнопку «Добавить услугу» в типовую Бухгалтерию или УТ лучше добавить расширением конфигурации: команда формы, обработчик на клиенте, при необходимости серверный вызов. Типовая остаётся на поддержке, обновления не ломают вашу доработку.
  • 📋 Табличная часть справочника. Всё сказанное работает и для справочников, например контактной информации или дополнительных реквизитов. Отличие одно: справочник записывают Записать() без режима проведения.
Где кодЧто доступноЧего нет
&НаКлиентеОбъект.Товары.Добавить(), ТекущиеДанные, ДобавитьСтроку()Запросы, разыменование ссылок
&НаСервереДанные формы, запросы, РеквизитФормыВЗначениеИнтерактивные методы таблицы
&НаСервереБезКонтекстаЗапросы, вычисления по параметрамОбъект формы и её элементы

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

Можно ли добавить строку в табличную часть проведённого документа обработкой?
Да. Получите объект через ПолучитьОбъект(), добавьте строку и запишите в режиме проведения, чтобы обновились движения. В закрытом периоде запись может быть запрещена датой запрета изменения.
Как добавить строку в табличную часть сразу у нескольких документов?
Выберите документы запросом, в цикле получите объект каждого, добавьте строку и запишите. Каждую запись оборачивайте в Попытка, чтобы ошибка одного документа не остановила остальные.
Есть ли ограничение на число строк в табличной части?
Жёсткого лимита для практических задач нет. Но документ на десятки тысяч строк долго открывается и проводится, поэтому такие объёмы обычно делят на несколько документов.
Чем отличается Добавить() у табличной части и ДобавитьСтроку() у таблицы формы?
Добавить() молча создаёт строку в данных. ДобавитьСтроку() имитирует нажатие пользователя: открывает строку на редактирование и вызывает события формы.
Почему после Загрузить() сбилась нумерация строк?
Не сбилась, а пересчитана: НомерСтроки присваивается заново по порядку загруженной таблицы. Нужен свой порядок — отсортируйте таблицу значений перед загрузкой.