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