К статье
Практический материал

Разработка и Конфигурация · 9 мин

Как скопировать документ программно в 1С

Как программно скопировать документ в 1С: метод Скопировать(), создание копии через Заполнить(), перенос табличных частей, проверка результата и типичные ошибки разработчика.

Копирование документа программным способом — типовая задача при доработке конфигураций 1С: создание корректировок, шаблонов на основании существующих документов, автоматическое дублирование заявок или перенос данных между периодами. Встроенный язык предоставляет несколько способов решения, и выбор зависит от того, нужна ли точная копия, копия с очисткой части реквизитов или заполнение по правилам «ввод на основании».

Основной инструмент — метод Скопировать() объекта типа «ДокументОбъект». Он создаёт новый объект документа со всеми реквизитами, табличными частями и движениями исходника, но без ссылки: документ считается новым и получит номер при записи. Альтернативы — заполнение через обработчик ОбработкаЗаполнения с передачей основания и ручной перенос данных, когда копия должна отличаться от оригинала.

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

Как работает метод Скопировать() у документа

Метод Скопировать() вызывается у объекта документа и возвращает новый объект того же вида, заполненный данными текущего. Копируются все реквизиты шапки, табличные части и наборы движений. Не копируются: ссылка (новый объект её не имеет до записи), номер (будет присвоен при записи), пометка удаления и факт проведения — новый документ всегда создаётся непроведённым.

Типовой фрагмент кода выглядит так:

ИсходныйДокумент = Документы.РеализацияТоваровУслуг.НайтиПоНомеру("0000000123", ТекущаяДатаСеанса());

Если Не ИсходныйДокумент.Пустая() Тогда

ИсходныйОбъект = ИсходныйДокумент.ПолучитьОбъект();

НовыйДокумент = ИсходныйОбъект.Скопировать();

НовыйДокумент.Дата = ТекущаяДатаСеанса();

НовыйДокумент.Записать(РежимЗаписиДокумента.Запись);

КонецЕсли;

bsl
НовыйДокумент = ИсходныйОбъект.Скопировать();
НовыйДокумент.Дата = ТекущаяДатаСеанса();
НовыйДокумент.Записать(РежимЗаписиДокумента.Запись);

Обратите внимание на дату: скопированный документ сохраняет дату оригинала, что почти всегда нежелательно. Устанавливайте актуальную дату явно, иначе документ «провалится» в прошлый период и может попасть в закрытый период, где запись будет запрещена.

Копирование через ОбработкаЗаполнения

Если копия нужна как часть бизнес-логики (например, «создать корректировку на основании реализации»), правильнее не копировать объект напрямую, а передать исходный документ как основание. Для этого используется метод Заполнить() или создание объекта с последующим программным заполнением:

НовыйДокумент = Документы.КорректировкаРеализации.СоздатьДокумент();

НовыйДокумент.Заполнить(ИсходныйДокументСсылка);

НовыйДокумент.Записать();

При таком вызове срабатывает обработчик ОбработкаЗаполнения в модуле объекта, где разработчик конфигурации описал, какие реквизиты и табличные части переносить. Это защищает от копирования лишнего: например, в корректировку не переедут признаки оплаты или служебные статусы исходника.

Копирование с изменением реквизитов и табличных частей

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

НовыйДокумент = ИсходныйОбъект.Скопировать();

НовыйДокумент.Дата = ТекущаяДатаСеанса();

НовыйДокумент.Склад = Справочники.Склады.НайтиПоНаименованию("Основной");

НовыйДокумент.Комментарий = "Создан копированием из " + ИсходныйОбъект.Номер;

// Удалить строки с нулевым количеством

Индекс = НовыйДокумент.Товары.Количество() - 1;

Пока Индекс >= 0 Цикл

Если НовыйДокумент.Товары[Индекс].Количество = 0 Тогда

НовыйДокумент.Товары.Удалить(Индекс);

КонецЕсли;

Индекс = Индекс - 1;

КонецЦикла;

НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);

Удалять строки табличной части нужно с конца (в обратном порядке индексов), иначе при удалении текущей строки индексы сдвинутся и часть строк будет пропущена. Это одна из самых частых ошибок начинающих разработчиков.

Типичные ошибки
01
Удаление строк табличной части в прямом цикле — индексы смещаются, часть строк пропускается; удаляйте с конца
02
Забытая установка новой даты — копия уходит в прошлый период и может не записаться из-за даты запрета изменения данных
03
Попытка провести копию без проверки заполнения — используйте ПроверитьЗаполнение() перед Записать(Проведение)
04
Вызов Скопировать() на клиенте — метод доступен только на сервере, оборачивайте в серверный вызов

Скопировать() против Заполнить() и ручного переноса

Сравнение вариантов

Скопировать()

Полная точная копия всех данных. Подходит для дублей и шаблонов. Ограничение: копирует всё, включая устаревшие и служебные значения

Заполнить(Основание)

Копия по правилам конфигурации через ОбработкаЗаполнения. Подходит для документов, связанных бизнес-логикой. Ограничение: переносит только то, что описано в обработчике

Ручной перенос реквизитов

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

Выбор способа определяется степенью сходства копии с оригиналом. Если отличий меньше трети реквизитов — берите Скопировать() и корректируйте. Если копия должна следовать правилам типовой конфигурации — Заполнить(). Если документы связаны слабо, явный перенос полей надёжнее: он документирует, какие данные действительно нужны.

Маршрут решения

Как выбрать способ

Нужна точная копия с минимальными правками (дата, склад, комментарий)Используйте Скопировать() и скорректируйте нужные реквизиты перед записью
Создаётся другой вид документа на основании исходногоИспользуйте Заполнить() с передачей ссылки на основание
Копия должна содержать только часть данных или другой набор строкСоздайте документ через СоздатьДокумент() и перенесите реквизиты вручную

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

Перед записью копии вызовите ПроверитьЗаполнение() — он вернёт Ложь, если обязательные реквизиты оказались пустыми (например, в исходнике поле было необязательным, а в текущих настройках стало обязательным). Записывайте документ в транзакции, если копирование — часть пакетной операции: при ошибке в середине цикла часть документов не «зависнет» в полузаписанном состоянии.

После записи убедитесь, что документ получил номер, ссылку и, при необходимости, провёлся. Признак успешного проведения — НовыйДокумент.Проведен = Истина после вызова Записать(РежимЗаписиДокумента.Проведение). Если проведение не выполнилось, платформа выдаст сообщения об ошибках — их нужно собрать и показать пользователю, а не молча оставлять документ непроведённым.

Контрольные точки

Как проверить результат

Ссылка и номер нового документа
Норма

Ссылка не пустая, номер присвоен автоматически

Тревожный признак

Ссылка пустая — документ не записался

Дата документа
Норма

Соответствует текущему рабочему периоду

Тревожный признак

Осталась дата исходного документа из прошлого периода

Проведение
Норма

Проведен = Истина, движения сформированы

Тревожный признак

Документ записан, но не проведён, сообщения об ошибках проигнорированы

Табличные части
Норма

Количество строк и итоги совпадают с ожидаемыми

Тревожный признак

Строки потеряны или задублированы после редактирования в цикле

Ограничения и риски

Метод Скопировать() недоступен в клиентском контексте управляемых форм — весь код копирования размещайте в серверных процедурах. При копировании документов с движениями по регистрам помните: наборы записей копируются, но при проведении движения будут пересчитаны заново по данным табличных частей, поэтому ручные корректировки движений исходника в копию фактически не переносятся.

Осторожно копируйте документы с уникальными идентификаторами в реквизитах (например, идентификаторы платежей, ГИСМ, ЕГАИС): дублирование таких значений может привести к отказу при обмене с внешними системами. Такие поля следует очищать после копирования.

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

Можно ли скопировать документ вместе с пометкой удаления или проведением?

Нет. Метод Скопировать() всегда возвращает новый непроведённый документ без пометки удаления. Если нужно сразу провести копию, вызовите Записать(РежимЗаписиДокумента.Проведение) после корректировки данных.

Почему скопированный документ не записывается с ошибкой о дате запрета?

Копия наследует дату исходного документа. Если исходник находится в закрытом периоде, запись блокируется датой запрета изменения данных. Решение — явно установить НовыйДокумент.Дата = ТекущаяДатаСеанса() до записи.

Как скопировать только часть строк табличной части?

После Скопировать() пройдите по табличной части в обратном цикле и удалите ненужные строки через Удалить(Индекс). Либо создайте документ через СоздатьДокумент() и добавляйте строки выборочно через Товары.Добавить() с заполнением из строк исходника.

Работает ли Скопировать() на тонком клиенте?

Нет, метод доступен только на сервере. В управляемой форме вызывайте серверную процедуру (директива &НаСервере или вызов серверного метода общего модуля), где выполняется ПолучитьОбъект(), Скопировать() и Записать().

Чем копирование отличается от ввода на основании?

Скопировать() создаёт документ того же вида с полным набором данных. Ввод на основании (через Заполнить) создаёт документ того же или другого вида по правилам, описанным в ОбработкаЗаполнения, и обычно фиксирует связь между документами в реквизите-основании.

Итог

Что важно запомнить

Базовый способ программного копирования документа в 1С — получить объект исходника и вызвать Скопировать(), затем обязательно установить новую дату, скорректировать отличающиеся реквизиты, проверить заполнение и записать. Для копий по бизнес-правилам используйте Заполнить() с основанием. Тестируйте пакетное копирование на копии базы.