Новая строка в табличной части: от метода Добавить() до загрузки тысяч позиций
Содержание
Чтобы добавить запись в табличную часть 1С из кода, вызовите Объект.ИмяТабличнойЧасти.Добавить(): метод вернёт новую пустую строку, в которую вы присваиваете значения реквизитов, а затем записываете объект. Пользователь в форме делает то же самое кнопкой Добавить над таблицей или клавишей Ins.
Способ выбирают по задаче. Одна-две строки по кнопке — Добавить() прямо в клиентской процедуре формы. Строка в определённом месте — Вставить(Индекс). Сотни и тысячи позиций — запрос, таблица значений и один вызов Загрузить(). Всё ниже относится к платформе 8.3 и управляемым формам, синтаксис одинаков для любой конфигурации: Бухгалтерии 3.0, ЗУП 3.1, УТ 11, ERP и самописных баз.
Как устроена табличная часть и какие методы добавляют строки
Табличная часть — это вложенная таблица внутри документа или справочника: «Товары» в реализации, «Начисления» в документе начисления зарплаты. У каждой строки есть служебный реквизит НомерСтроки, он заполняется платформой сам. Остальные колонки — реквизиты, которые разработчик описал в Конфигураторе.
Строки живут только вместе с объектом. Отдельно записать строку нельзя: вы меняете коллекцию и записываете весь документ целиком.
| Метод | Что делает | Когда нужен |
|---|---|---|
Добавить() | Новая строка в конец | Основной случай |
Вставить(Индекс) | Новая строка на позицию, индекс с нуля | Строка-заголовок, вставка перед выбранной |
Загрузить(ТаблицаЗначений) | Заменяет всё содержимое строками таблицы | Массовое заполнение |
Сдвинуть(Строка, Смещение) | Перемещает строку вверх или вниз | Ручная сортировка |
Очистить() | Удаляет все строки | Перезаполнение с нуля |
Минимальный пример на сервере, когда у вас есть ссылка на документ:
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
НоваяСтрока = ДокументОбъект.Товары.Добавить();
НоваяСтрока.Номенклатура = Номенклатура;
НоваяСтрока.Количество = 5;
НоваяСтрока.Цена = 120;
НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена; // 600
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
Ключевой шаг — ПолучитьОбъект(). Через ссылку табличная часть доступна только на чтение, добавить в неё строку нельзя.
Если документ уже проведён, записывайте его в режиме РежимЗаписиДокумента.Проведение. Иначе табличная часть изменится, а движения по регистрам останутся старыми.
Вставка на конкретное место выглядит так. Индексы считаются с нуля, поэтому Вставить(0) даёт первую строку:
ПерваяСтрока = ДокументОбъект.Товары.Вставить(0);
ПерваяСтрока.Содержание = "Доставка";
Добавление строк в форме: что делает пользователь
У пользователя три штатных пути добавить запись в табличную часть:
- ➕ кнопка Добавить в командной панели над таблицей или клавиша
Ins— пустая строка в режиме редактирования; - 📄 копирование текущей строки клавишей
F9— новая строка с теми же значениями, удобно для позиций с одной ценой; - 🔍 кнопка Подбор в типовых документах — окно выбора номенклатуры, откуда переносится сразу много позиций.
После ввода значения в ячейку платформа вызывает обработчик ПриИзменении этой колонки. Именно там в типовых конфигурациях пересчитывается сумма, подставляется ставка НДС и единица измерения. Если строка добавлена кодом, эти обработчики не срабатывают — к этому вернёмся в разделе про ошибки.
Строка не уйдёт в базу, пока пользователь не нажмёт Записать или Провести. До этого все изменения хранятся в данных формы.
Программное добавление записи в табличную часть управляемой формы
В управляемой форме Объект — это не документ, а его копия в данных формы. Табличная часть там имеет тип ДанныеФормыКоллекция. Метод Добавить() у неё работает и на клиенте, и на сервере.
Добавить строку по своей кнопке
Процедура на клиенте: строка добавляется, курсор ставится на неё, форма помечается изменённой.
&НаКлиенте
Процедура ДобавитьУслугу(Команда)
НоваяСтрока = Объект.Услуги.Добавить();
НоваяСтрока.Количество = 1;
Элементы.Услуги.ТекущаяСтрока = НоваяСтрока.ПолучитьИдентификатор();
Модифицированность = Истина;
КонецПроцедуры
ТекущаяСтрока у таблицы формы принимает не саму строку, а её идентификатор. Поэтому нужен ПолучитьИдентификатор().
Если нужно, чтобы пользователь сразу начал вводить значения в новой строке, вызовите у элемента формы Элементы.Услуги.ДобавитьСтроку(). Это то же самое, что нажать Ins: сработают все интерактивные события.
Подставить значения по умолчанию при интерактивном добавлении
Для этого есть событие таблицы формы ПриНачалеРедактирования. Параметр НоваяСтрока отличает новую строку от редактирования старой, Копирование — копию от чистой строки.
&НаКлиенте
Процедура ТоварыПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)
Если НоваяСтрока И Не Копирование Тогда
Элемент.ТекущиеДанные.Количество = 1;
Элемент.ТекущиеДанные.Склад = Объект.Склад;
КонецЕсли;
КонецПроцедуры
Запретить добавление по условию можно в событии ПередНачаломДобавления: установите там Отказ = Истина.
Когда нужен сервер
На клиенте недоступны запросы и обращение к реквизитам ссылок через точку. Цену, ставку НДС, остаток получают в серверной процедуре. Если серверу контекст формы не нужен, используйте &НаСервереБезКонтекста: так на сервер не уходят данные всей формы.
Если серверный код работает с методами модуля объекта (например, процедурой заполнения), получите сам объект:
&НаСервере
Процедура ЗаполнитьПоОснованию()
ДокОбъект = РеквизитФормыВЗначение("Объект");
ДокОбъект.ЗаполнитьТоварыПоОснованию(); // ваша экспортная процедура модуля объекта
ЗначениеВРеквизитФормы(ДокОбъект, "Объект");
КонецПроцедуры
После ЗначениеВРеквизитФормы строки табличной части получают новые идентификаторы. Сохранённые раньше значения ТекущаяСтрока становятся недействительными — получайте их заново.
Заполнение обязательных реквизитов строки и типы значений
Пустая строка после Добавить() содержит пустые значения своих типов: 0 для чисел, пустую строку, пустую ссылку, Неопределено для составного типа. Сумма сама не посчитается, ставка НДС не подставится.
Что проверить у каждой колонки
- 🔢 Число — длина и точность из Конфигуратора. Если у реквизита 2 знака после запятой, а вы присваиваете результат деления, округлите явно:
Окр(Сумма / Количество, 2). - 🔤 Строка фиксированной длины — значение длиннее лимита обрежется без предупреждения. Для комментариев с длинным текстом проверьте длину заранее через
СтрДлина(). - 🔗 Ссылка — присваивайте значение именно того типа, что указан. Справочник «Контрагенты» в поле «Номенклатура» не встанет.
- 🧩 Составной тип — допустим любой из перечисленных типов. Если код ниже по тексту ждёт конкретный, проверяйте
ТипЗнч(Значение) = Тип("СправочникСсылка.Номенклатура").
Не дублируйте логику пересчёта
Ручной пересчёт в каждом месте, где добавляются строки, со временем расходится с формой. Вынесите расчёт суммы и НДС в одну экспортную процедуру общего модуля и вызывайте её и из ПриИзменении, и из программного заполнения.
Для перенесения значений из другой коллекции с теми же именами колонок есть ЗаполнитьЗначенияСвойств(Приёмник, Источник):
Для Каждого СтрокаИсточника Из ТаблицаИсточник Цикл
НоваяСтрока = Объект.Товары.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаИсточника);
КонецЦикла;
Третьим параметром можно перечислить через запятую только нужные свойства, четвёртым — исключить лишние.
Массовая загрузка тысяч строк без тормозов
Сам вызов Добавить() быстрый. Медленно становится из-за того, что делают вокруг него.
Три приёма, которые дают основной выигрыш
- Один запрос вместо обращений через точку. Строка
Номенклатура.ЕдиницаИзмерениявнутри цикла читает элемент справочника из базы. На 5 000 строк это 5 000 обращений. Получите все нужные поля одним запросом. Загрузить()вместо цикла. Если данные уже лежат в таблице значений с теми же именами колонок, одной командой замените ими содержимое табличной части.- Один вызов сервера. Не добавляйте строки на клиенте с отдельным серверным вызовом за ценой каждой строки. Передайте на сервер весь список и заполните табличную часть там.
Пример заполнения из запроса. Имя регистра в примере условное — подставьте своё:
&НаСервере
Процедура ЗаполнитьОстатками()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Остатки.Номенклатура КАК Номенклатура,
| Остатки.КоличествоОстаток КАК Количество
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(, Склад = &Склад) КАК Остатки";
Запрос.УстановитьПараметр("Склад", Объект.Склад);
Объект.Товары.Загрузить(Запрос.Выполнить().Выгрузить());
КонецПроцедуры
Колонки сопоставляются по имени. Поэтому в запросе важны псевдонимы КАК Количество: без них колонка не попадёт в нужный реквизит. Колонки, которых в табличной части нет, игнорируются. Реквизиты, которых нет в таблице, останутся пустыми.
Загрузить() удаляет все строки, которые были в табличной части до вызова. Если нужно дописать, а не заменить, сначала выгрузите текущие строки через Выгрузить(), добавьте новые в таблицу значений и загрузите результат обратно.
Схлопнуть повторы перед загрузкой
Файл от поставщика часто содержит одну позицию несколькими строками. Сверните таблицу перед загрузкой:
Таблица.Свернуть("Номенклатура, Цена", "Количество, Сумма");
Объект.Товары.Загрузить(Таблица);
Для поиска по большой таблице в цикле вместо перебора используйте Соответствие с ключом-ссылкой или индекс таблицы значений Таблица.Индексы.Добавить("Номенклатура").
Проверка строк перед записью и обработка ошибок
Платформа сама проверит реквизиты, у которых в Конфигураторе свойство «Проверка заполнения» установлено в «Выдавать ошибку». Для своих правил есть обработчик ОбработкаПроверкиЗаполнения в модуле объекта.
Пример: количество в каждой строке должно быть больше нуля. Сообщение привязывается к ячейке, и пользователь видит, где ошибка:
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
Для Каждого Строка Из Товары Цикл
Если Строка.Количество <= 0 Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Строка " + Формат(Строка.НомерСтроки, "ЧГ=0")
+ ": количество должно быть больше нуля";
Сообщение.Поле = "Товары[" + Формат(Строка.НомерСтроки - 1, "ЧГ=0") + "].Количество";
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();
Отказ = Истина;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
В поле сообщения индекс строки начинается с нуля, а НомерСтроки — с единицы. Отсюда вычитание единицы.
ОбработкаПроверкиЗаполнения вызывается при интерактивной записи и при явном вызове ПроверитьЗаполнение(). Документ, который код записывает в режиме проведения, тоже проходит проверку. А вот обычная запись из кода через Записать() без проведения её не запускает. Если строки добавляет обработка, вызовите ДокументОбъект.ПроверитьЗаполнение() перед записью.
При загрузке из внешних источников оборачивайте запись в Попытка … Исключение и пишите текст ошибки в журнал регистрации через ЗаписьЖурналаРегистрации(). Одна битая позиция не должна молча обрывать загрузку всего файла.
Особенности управляемых форм, о которых забывают
Управляемая форма делит код на клиент и сервер, и это влияет на добавление строк сильнее, чем кажется.
- 🧭 Текущая строка. На клиенте данные выделенной строки берут через
Элементы.Товары.ТекущиеДанные. Если строки не выделено, тамНеопределено— проверяйте перед обращением. - 🔒 Только просмотр. Если у таблицы формы стоит свойство «Только просмотр», пользователь не добавит строку кнопкой, но код по-прежнему может. Для запрета на всех уровнях проверяйте условие в
ПередЗаписьюобъекта. - 🧱 Расширение вместо правки типовой. Свою кнопку «Добавить услугу» в типовую Бухгалтерию или УТ лучше добавить расширением конфигурации: команда формы, обработчик на клиенте, при необходимости серверный вызов. Типовая остаётся на поддержке, обновления не ломают вашу доработку.
- 📋 Табличная часть справочника. Всё сказанное работает и для справочников, например контактной информации или дополнительных реквизитов. Отличие одно: справочник записывают
Записать()без режима проведения.
| Где код | Что доступно | Чего нет |
|---|---|---|
&НаКлиенте | Объект.Товары.Добавить(), ТекущиеДанные, ДобавитьСтроку() | Запросы, разыменование ссылок |
&НаСервере | Данные формы, запросы, РеквизитФормыВЗначение | Интерактивные методы таблицы |
&НаСервереБезКонтекста | Запросы, вычисления по параметрам | Объект формы и её элементы |
Частые вопросы
Можно ли добавить строку в табличную часть проведённого документа обработкой?
ПолучитьОбъект(), добавьте строку и запишите в режиме проведения, чтобы обновились движения. В закрытом периоде запись может быть запрещена датой запрета изменения.Как добавить строку в табличную часть сразу у нескольких документов?
Попытка, чтобы ошибка одного документа не остановила остальные.Есть ли ограничение на число строк в табличной части?
Чем отличается Добавить() у табличной части и ДобавитьСтроку() у таблицы формы?
Добавить() молча создаёт строку в данных. ДобавитьСтроку() имитирует нажатие пользователя: открывает строку на редактирование и вызывает события формы.Почему после Загрузить() сбилась нумерация строк?
НомерСтроки присваивается заново по порядку загруженной таблицы. Нужен свой порядок — отсортируйте таблицу значений перед загрузкой.