Дублируем строки ТаблицыЗначений во встроенном языке 1С:Предприятия 8.3
Содержание
Чтобы скопировать строку таблицы значений в 1С, создайте новую строку методом Добавить() или Вставить() и перенесите в неё значения одной командой ЗаполнитьЗначенияСвойств(НоваяСтрока, ИсходнаяСтрока). Отдельного метода «скопировать строку» у объекта ТаблицаЗначений нет: метод Скопировать() возвращает новую таблицу целиком, а не строку.
Всё ниже относится к платформе 1С:Предприятие 8.3 и не зависит от конфигурации: код одинаково работает в БП 3.0, ЗУП 3.1, УТ 11 и в самописных решениях. Отдельно разобраны случаи, на которых чаще всего ошибаются: типизированные колонки, вложенные таблицы, копирование на управляемой форме и массовое копирование тысяч строк.
Базовый способ скопировать строку таблицы значений
Минимальный рабочий вариант — три строки кода. Новая строка встаёт в конец таблицы:
НоваяСтрока = ТЗ.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ИсходнаяСтрока);
Здесь ИсходнаяСтрока — объект типа СтрокаТаблицыЗначений. Получить его можно несколькими путями:
- 🔢 по индексу:
ИсходнаяСтрока = ТЗ[0];(нумерация с нуля); - 🔍 поиском по значению:
ИсходнаяСтрока = ТЗ.Найти(Номенклатура, "Номенклатура");; - 📋 из массива найденных строк:
ТЗ.НайтиСтроки(Новый Структура("Склад", Склад)); - 🔁 в цикле
Для Каждого Строка Из ТЗ Цикл.
Копия сразу под исходной строкой
Если дубль нужен не в конце, а рядом с оригиналом, используйте Вставить() с индексом на единицу больше:
Индекс = ТЗ.Индекс(ИсходнаяСтрока);
НоваяСтрока = ТЗ.Вставить(Индекс + 1);
ЗаполнитьЗначенияСвойств(НоваяСтрока, ИсходнаяСтрока);
Метод Индекс() возвращает позицию строки в таблице. Вставить(Индекс + 1) сдвигает все строки ниже на одну позицию вниз. Если исходная строка последняя, вставка с индексом, равным количеству строк, отработает как обычное добавление в конец.
Готовую строку можно передвинуть позже методом ТЗ.Сдвинуть(НоваяСтрока, -1): отрицательное смещение двигает строку вверх, положительное — вниз.
Почему не работает «НоваяСтрока = ИсходнаяСтрока»
Присваивание не копирует данные. Обе переменные начинают указывать на одну и ту же строку таблицы. Меняете НоваяСтрока.Количество — меняется и «оригинал», потому что это один объект. Новая строка в таблице при этом не появляется.
Копирование строки с заменой значений отдельных полей
На практике строку редко дублируют один в один. Обычно копию делают, чтобы поменять в ней одно-два поля: разбить количество на две партии, перенести остаток на другой склад, разделить сумму по статьям.
Порядок такой: сначала копируете всё, затем перезаписываете нужные колонки.
// Разбиваем строку на две: 30% уходит на второй склад
НоваяСтрока = ТЗ.Вставить(ТЗ.Индекс(ИсходнаяСтрока) + 1);
ЗаполнитьЗначенияСвойств(НоваяСтрока, ИсходнаяСтрока);
НоваяСтрока.Склад = СкладРезерва;
НоваяСтрока.Количество = Окр(ИсходнаяСтрока.Количество * 0.3, 3);
НоваяСтрока.Сумма = Окр(ИсходнаяСтрока.Сумма * 0.3, 2);
ИсходнаяСтрока.Количество = ИсходнаяСтрока.Количество - НоваяСтрока.Количество;
ИсходнаяСтрока.Сумма = ИсходнаяСтрока.Сумма - НоваяСтрока.Сумма;
Проверим на числах. Было 10 шт. на 1 000 руб. В копию уйдёт 3 шт. и 300 руб., в оригинале останется 7 шт. и 700 руб. Итог по таблице не изменился: 10 шт. и 1 000 руб.
Остаток считайте вычитанием, как в примере, а не вторым умножением на 0.7. Иначе на суммах вроде 333,33 руб. копейки при округлении разойдутся, и итог по таблице перестанет совпадать с исходным.
Копирование без части колонок
У ЗаполнитьЗначенияСвойств четыре параметра:
| Параметр | Что передать | Пример |
|---|---|---|
| Приёмник | строку, в которую пишем | НоваяСтрока |
| Источник | строку, из которой читаем | ИсходнаяСтрока |
| Список свойств | имена через запятую — копировать только их | "Номенклатура, Цена" |
| Исключая свойства | имена через запятую — копировать всё, кроме них | "Количество, Сумма" |
Третий и четвёртый параметры необязательные. Если нужен только четвёртый, третий оставляют пустым:
// Копия без количества и суммы: их заполнит пользователь
ЗаполнитьЗначенияСвойств(НоваяСтрока, ИсходнаяСтрока, , "Количество, Сумма");
Исключённые колонки в новой строке получат пустое значение своего типа. Подробнее о том, какое именно, — ниже, в разделе про типы.
Ручной перенос значений по колонкам
ЗаполнитьЗначенияСвойств удобна, но иногда колонки нужно обойти явно. Например, когда к значению применяется преобразование или имена колонок вычисляются в коде.
Обход по коллекции колонок:
НоваяСтрока = ТЗ.Добавить();
Для Каждого Колонка Из ТЗ.Колонки Цикл
НоваяСтрока[Колонка.Имя] = ИсходнаяСтрока[Колонка.Имя];
КонецЦикла;
К значению строки можно обращаться тремя способами:
- Через точку:
Строка.Цена— когда имя колонки известно при написании кода. - Через квадратные скобки с именем:
Строка["Цена"]илиСтрока[Колонка.Имя]— когда имя приходит из переменной. - Через квадратные скобки с индексом колонки:
Строка[0]— работает, но код плохо читается и ломается при изменении порядка колонок.
Когда ручной обход оправдан
- 🔄 Нужно на лету поменять значение: обрезать строку, пересчитать валюту, заменить пустую ссылку на значение по умолчанию.
- 🧩 В исходной и целевой таблицах колонки называются по-разному, и есть соответствие имён.
- 🧪 Нужно пропустить колонки по условию, например все, у которых имя начинается с
Служебное.
Пример с соответствием имён колонок:
Соответствие = Новый Соответствие;
Соответствие.Вставить("Товар", "Номенклатура");
Соответствие.Вставить("Кол", "Количество");
НоваяСтрока = ТЗПриёмник.Добавить();
Для Каждого Пара Из Соответствие Цикл
НоваяСтрока[Пара.Значение] = СтрокаИсточник[Пара.Ключ];
КонецЦикла;
Если же имена совпадают и преобразований нет, ручной цикл — лишний код. Платформенная функция сделает то же самое короче.
ЗаполнитьЗначенияСвойств между разными таблицами
Главное преимущество функции — она сопоставляет колонки по имени. Это позволяет копировать строку таблицы значений в таблицу с другим набором колонок, в табличную часть документа или в структуру.
Что произойдёт с несовпадающими колонками
- ✅ Колонки, которые есть и в источнике, и в приёмнике, копируются.
- ➖ Колонки, которых нет в приёмнике, просто не переносятся.
- ⬜ Колонки приёмника, которых нет в источнике, остаются со значениями по умолчанию.
- 🔤 Регистр букв в именах роли не играет:
ЦенаиЦЕНАдля 1С одно и то же имя.
Если вы передаёте явный список свойств, перечисляйте только колонки, которые реально есть в обеих коллекциях. Опечатка в имени не всегда очевидна при чтении кода, а проверять её придётся в отладчике.
Перенос строки в табличную часть документа
Типичная задача: подобрали данные запросом в таблицу значений и переносите в табличную часть Товары:
Для Каждого СтрокаТЗ Из ТЗ Цикл
СтрокаТЧ = ДокументОбъект.Товары.Добавить();
ЗаполнитьЗначенияСвойств(СтрокаТЧ, СтрокаТЗ);
КонецЦикла;
НомерСтроки табличная часть проставит сама. Лишние колонки таблицы значений (например, служебный Остаток, по которому вы фильтровали) в документ не попадут, потому что в табличной части их нет.
Копирование в структуру и обратно
Источником и приёмником могут быть не только строки. Функция работает с любыми объектами, у которых есть свойства: структурами, строками табличных частей, объектами справочников и документов.
// Запомнить строку, удалить её и вернуть позже
Слепок = Новый Структура("Номенклатура, Количество, Цена, Сумма");
ЗаполнитьЗначенияСвойств(Слепок, ИсходнаяСтрока);
У структуры заполнятся только заранее объявленные ключи. Пустая Новый Структура ничего не получит: ключей в ней нет, а сама функция новых ключей не создаёт.
Пустые значения, типы колонок и вложенные таблицы
Скопированное значение зависит от того, задан ли у колонки тип. Это частая причина «странных» нулей и пустых строк после копирования.
Колонки с типом и без
При создании колонки тип задают третьим параметром не всегда. Сравните:
ТЗ.Колонки.Добавить("Комментарий"); // тип не задан
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число",
Новый КвалификаторыЧисла(15, 3)));
| Колонка | Значение в новой пустой строке | Что будет при записи значения другого типа |
|---|---|---|
| без типа | Неопределено | запишется как есть |
Число | 0 | значение приведётся к числу |
Строка | "" | значение приведётся к строке |
Дата | пустая дата '00010101' | значение приведётся к дате |
| ссылочный тип | пустая ссылка этого типа | значение приведётся к типу колонки |
Отсюда два практических следствия:
- Если в исходной таблице колонка без типа хранит
Неопределено, а в приёмнике колонкаЧисло, после копирования там будет0. ПроверкаЗначение = Неопределенов приёмнике больше не сработает. - Квалификаторы тоже применяются. В колонке с квалификатором
(15, 2)не сохранятся лишние знаки после запятой, а строка длиннее заданной длины будет обрезана.
Для проверки «значение не заполнено» используйте ЗначениеЗаполнено(НоваяСтрока.Склад). Функция корректно отвечает и для Неопределено, и для 0, и для пустой ссылки.
Ссылки копируются ссылками
Если в колонке лежит СправочникСсылка.Номенклатура, в новую строку попадёт та же ссылка. Это правильно: элемент справочника один, на него просто указывают две строки. Копию самого элемента справочника при этом никто не создаёт.
Вложенная таблица в ячейке — частая ловушка
Колонка может хранить другую таблицу значений, массив или структуру. При копировании строки переносится ссылка на этот объект, а не его копия. Обе строки начинают делить одну вложенную таблицу.
Изменили вложенную таблицу в копии — изменилась и в оригинале. Чтобы этого не было, вложенную коллекцию копируют явно:
НоваяСтрока = ТЗ.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ИсходнаяСтрока);
Если ТипЗнч(ИсходнаяСтрока.Серии) = Тип("ТаблицаЗначений") Тогда
НоваяСтрока.Серии = ИсходнаяСтрока.Серии.Скопировать();
КонецЕсли;
Для массива независимую копию создают через новый массив и цикл добавления элементов. Для структуры — через новую структуру и ЗаполнитьЗначенияСвойств после объявления ключей. При глубокой вложенности (таблица в таблице в таблице) копируйте каждый уровень.
Копирование строки на управляемой форме
На клиенте объекта ТаблицаЗначений нет. Реквизит формы такого типа на клиенте виден как ДанныеФормыКоллекция, а строки — как ДанныеФормыЭлементКоллекции. Хорошая новость: методы Добавить(), Вставить(), Индекс() и функция ЗаполнитьЗначенияСвойств у них есть, поэтому базовый код почти не меняется.
&НаКлиенте
Процедура ДублироватьСтроку(Команда)
ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
Индекс = Товары.Индекс(ТекущиеДанные);
НоваяСтрока = Товары.Вставить(Индекс + 1);
ЗаполнитьЗначенияСвойств(НоваяСтрока, ТекущиеДанные);
Элементы.Товары.ТекущаяСтрока = НоваяСтрока.ПолучитьИдентификатор();
КонецПроцедуры
Последняя строка переводит курсор на созданную копию. Без неё пользователь продолжит стоять на исходной строке и будет уверен, что ничего не произошло.
Интерактивное копирование и очистка полей
Пользователь копирует строку в табличном поле клавишей F9 или командой копирования в командной панели таблицы. Программно то же действие запускает метод элемента формы Элементы.Товары.СкопироватьСтроку() — он открывает новую строку на редактирование, как будто пользователь нажал F9.
Чтобы при копировании очищались отдельные поля, используйте событие ПриНачалеРедактирования. У него есть параметры НоваяСтрока и Копирование:
&НаКлиенте
Процедура ТоварыПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)
Если НоваяСтрока И Копирование Тогда
Элемент.ТекущиеДанные.Количество = 0;
Элемент.ТекущиеДанные.Сумма = 0;
КонецЕсли;
КонецПроцедуры
Запретить копирование совсем можно в обработчике ПередНачаломДобавления: если параметр Копирование равен Истина, установите Отказ = Истина.
Когда нужен сервер
Если над таблицей нужны методы, которых у коллекции формы нет (Скопировать(), Свернуть(), ВыгрузитьКолонку()), переходите на сервер и преобразуйте реквизит:
&НаСервере
Процедура ОбработатьНаСервере()
ТЗ = РеквизитФормыВЗначение("Товары");
// ... работа с ТаблицаЗначений
ЗначениеВРеквизитФормы(ТЗ, "Товары");
КонецПроцедуры
После ЗначениеВРеквизитФормы строки получат новые идентификаторы, поэтому текущую строку на форме придётся установить заново.
Массовое копирование строк и скорость
Когда речь о сотнях и тысячах строк, выбор способа заметно влияет на время работы. Правило простое: чем больше работы делает платформа одним вызовом, тем быстрее.
| Задача | Способ | Комментарий |
|---|---|---|
| копия всей таблицы | ТЗ.Скопировать() | один вызов, колонки и строки |
| только структура колонок | ТЗ.СкопироватьКолонки() | пустая таблица с теми же колонками и типами |
| строки по условию | ТЗ.Скопировать(Новый Структура("Склад", Склад)) | отбор по равенству значений |
| строки из массива | ТЗ.Скопировать(МассивСтрок) | массив, например, из НайтиСтроки() |
| часть колонок | ТЗ.Скопировать(, "Номенклатура, Количество") | второй параметр — список колонок |
| в другую таблицу с другим составом | цикл Добавить() + ЗаполнитьЗначенияСвойств | по одной строке |
| одна колонка целиком | ВыгрузитьКолонку() / ЗагрузитьКолонку() | через массив |
Дублирование строк внутри той же таблицы
Нельзя обходить таблицу циклом Для Каждого и тут же добавлять в неё строки. Новые строки окажутся в той же коллекции, которую вы обходите, и результат будет непредсказуемым.
Надёжно — зафиксировать количество строк до цикла и идти по индексам:
ИсходноеКоличество = ТЗ.Количество();
Для Сч = 0 По ИсходноеКоличество - 1 Цикл
НоваяСтрока = ТЗ.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ТЗ[Сч]);
КонецЦикла;
Индексы исходных строк при добавлении в конец не меняются, так что ТЗ[Сч] всегда указывает на оригинал. Таблица из 500 строк станет таблицей из 1 000.
Если копии должны стоять сразу под оригиналами, идите с конца. Тогда вставка не сдвигает ещё не обработанные строки. Количество строк сохраните до цикла:
ИсходноеКоличество = ТЗ.Количество();
Для Сч = 1 По ИсходноеКоличество Цикл
Индекс = ИсходноеКоличество - Сч;
НоваяСтрока = ТЗ.Вставить(Индекс + 1);
ЗаполнитьЗначенияСвойств(НоваяСтрока, ТЗ[Индекс]);
КонецЦикла;
Проверим на трёх строках A, B, C. Шаг 1: индекс 2, после C вставляется C′. Шаг 2: индекс 1, после B вставляется B′. Шаг 3: индекс 0, после A вставляется A′. Итог: A, A′, B, B′, C, C′. Строки выше текущей вставкой не затрагиваются, поэтому индексы считаются верно.
Что тормозит при больших объёмах
- 🐢 Вставка в начало или середину в цикле: каждая
Вставить()сдвигает все строки ниже. На десятках тысяч строк выгоднее добавлять в конец, а потом один раз вызватьТЗ.Сортировать("Номенклатура, Порядок"). - 🐢 Копирование на клиенте с вызовом сервера на каждую строку. Соберите строки на клиенте или сразу выполните всю обработку одним серверным вызовом.
- 🐢 Копирование строк ради последующего отбора и группировки. Если данные пришли из базы, запрос с
СГРУППИРОВАТЬ ПОи условиями отработает быстрее цикла, а результат выгружается в таблицу черезРезультат.Выгрузить(). - 🐢
Найти()внутри цикла по большой таблице. Добавьте индекс:ТЗ.Индексы.Добавить("Номенклатура")— поиск по индексированной колонке быстрее перебора.
Частые вопросы
Есть ли у таблицы значений метод КопироватьСтроку?
ТаблицаЗначений такого метода нет. Есть Скопировать(), который возвращает новую таблицу. Метод СкопироватьСтроку() есть у элемента формы ТаблицаФормы, и он запускает интерактивное копирование, как клавиша F9.Как скопировать строку из одной таблицы значений в другую?
Добавить() и вызовите ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаИсточника). Колонки сопоставятся по именам, лишние будут пропущены.Почему после копирования вместо Неопределено в ячейке стоит 0?
Число. Платформа приводит записываемое значение к типу колонки, и Неопределено превращается в 0. Для проверки заполненности используйте ЗначениеЗаполнено().Как получить таблицу только с выбранными строками?
НайтиСтроки(), и передайте его в ТЗ.Скопировать(МассивСтрок). Получится новая таблица с теми же колонками и только этими строками.Как удалить скопированную строку, если она оказалась лишней?
ТЗ.Удалить(НоваяСтрока). Метод принимает саму строку или её индекс. При удалении в цикле идите по таблице с конца, чтобы не пропускать строки.