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