Как создать новый документ в 1С 8.3, имея только ссылку на существующий документ того же типа?

Программист 1С v8.3 (Управляемые формы)
← На главную

Когда мы разрабатываем функционал в 1С:Предприятии 8.3, часто возникает задача создать новый документ, тип которого должен быть таким же, как у уже существующего документа, на который у нас есть только ссылка. Например, нам передали ссылку на документ "РасходнаяНакладная", и нам нужно создать еще один пустой документ "РасходнаяНакладная". Разберем, как это можно реализовать, избегая распространенных ошибок и понимая внутренние механизмы платформы.

Основные понятия 1С: ДокументСсылка, ДокументОбъект, МенеджерДокумента

Прежде чем мы перейдем к решению, давайте разберемся в ключевой терминологии 1С, связанной с документами, поскольку их правильное понимание критически важно для эффективной работы с платформой.

  1. ДокументСсылка (например, ДокументСсылка.РеализацияТоваровУслуг)
  2. Это облегченный тип, который используется для хранения ссылок на объекты базы данных. Представьте ее как уникальный адрес документа в базе данных. Ссылка содержит только идентификатор документа (УникальныйИдентификатор) и основные, служебные свойства. Мы используем ссылки в реквизитах других объектов, в параметрах процедур и функций, а также для отображения документов в списках. Ключевое отличие: ДокументСсылка не содержит всех данных документа (например, табличные части) и не предназначена для непосредственного изменения или записи данных. Она лишь "указывает" на документ.

  3. ДокументОбъект (например, ДокументОбъект.РеализацияТоваровУслуг)
  4. Это "полноценный" экземпляр документа, который загружается из базы данных со всеми его реквизитами, табличными частями и прочими данными. Мы получаем ДокументОбъект, когда нам нужно прочитать, изменить, записать, провести или удалить конкретный документ. Получение ДокументОбъекта из ДокументСсылки осуществляется методом ПолучитьОбъект(). Работа с ДокументОбъектом более ресурсоемка, поскольку требует загрузки всех данных в оперативную память.

  5. МенеджерДокумента (например, Документы.РеализацияТоваровУслуг)
  6. Это объект, который представляет вид документа в целом, как элемент метаданных конфигурации (то есть, не конкретный документ, а "тип документа"). Через менеджер документа мы выполняем глобальные операции, такие как создание нового документа данного типа (метод СоздатьДокумент()), поиск документов по определенным критериям (например, НайтиПоНомеру(), НайтиПоРеквизиту()) или получение пустой ссылки на документ. Менеджер документов является точкой входа для работы со всеми документами определенного вида.

    Важно понимать, что 1С:Предприятие не является чисто объектно-ориентированным языком в классическом понимании (как Java или C#). Поэтому такие формулировки, как "Класс Документы (Объект)", могут быть немного некорректными. Правильнее говорить о типах платформы: тип ДокументСсылка, тип ДокументОбъект и объект МенеджерДокумента, который представляет собой метаданные типа.

Решение: Создание нового документа по типу существующей ссылки

Теперь, когда мы разобрались с терминологией, рассмотрим основной сценарий: как создать новый документ того же типа, что и документ, на который у нас есть ДокументСсылка. Задача состоит в том, чтобы динамически определить тип документа из ссылки и затем использовать менеджер документов для создания нового экземпляра.

Разберем решение по шагам:

  1. Передаем ссылку на сервер: Мы должны передать ДокументСсылку в серверную процедуру, так как работа с метаданными и создание объектов документов происходит на сервере.
  2. Определяем имя типа документа: Используем свойство Метаданные у ДокументСсылки, чтобы получить объект метаданных, а затем свойство Имя этого объекта, чтобы узнать строковое имя вида документа (например, "РеализацияТоваровУслуг").
  3. Обращаемся к менеджеру документов: Используя полученное имя, мы динамически обращаемся к нужному менеджеру документов через глобальный объект Документы (это коллекция всех менеджеров документов).
  4. Вызываем метод СоздатьДокумент(): У менеджера документов вызываем метод СоздатьДокумент(), который вернет нам новый, еще не записанный в базу ДокументОбъект.
  5. Заполняем и записываем документ: После создания объекта, мы можем заполнить его необходимыми данными и обязательно записать в базу данных методом Записать().

Посмотрим на пример кода:


&НаКлиенте
Процедура СоздатьДокумент(Команда)
    // Предположим, ДокументСсылка получена из какого-то источника,
    // например, из текущего элемента списка или путем поиска.
    // Для примера, получим ссылку на первый попавшийся документ "РеализацияТоваровУслуг"
    // (в реальной задаче она будет передаваться)
    ДокументСсылка = Документы.РеализацияТоваровУслуг.НайтиПоНомеру("000000001", НачалоГода(ТекущаяДата()));
    Если ДокументСсылка.Пустая() Тогда
        Сообщить("Для примера не найдена ссылка на документ. Пожалуйста, убедитесь, что документ с номером 000000001 существует.");
        Возврат;
    КонецЕсли;

    СоздатьДокументНаСервере(ДокументСсылка);
КонецПроцедуры

&НаСервере
Процедура СоздатьДокументНаСервере(ДокументСсылка)
    // Шаг 1: Убедимся, что ссылка не пустая, прежде чем пытаться получить метаданные
    Если ДокументСсылка.Пустая() Тогда
        Сообщить("Передана пустая ссылка на документ. Создание нового документа невозможно.");
        Возврат;
    КонецЕсли;

    // Шаг 2: Получаем имя типа документа из метаданных ссылки
    // Свойство Метаданные() возвращает объект МетаданныеОбъектДокумент.
    // У этого объекта есть свойство Имя, которое и содержит строковое имя вида документа.
    ИмяТипаДокумента = ДокументСсылка.Метаданные().Имя;
    Сообщить("Определен тип документа: " + ИмяТипаДокумента);

    // Шаг 3 и 4: Через менеджер документов обращаемся к конкретному виду документа по его имени
    // и вызываем метод СоздатьДокумент().
    // Конструкция Документы[ИмяТипаДокумента] позволяет динамически получить
    // менеджер нужного вида документа, не зная его имя заранее.
    НовыйДокОбъект = Документы[ИмяТипаДокумента].СоздатьДокумент();

    // Шаг 5: Теперь у нас есть новый, еще не записанный в базу документ-объект.
    // Мы можем заполнить его данными:
    НовыйДокОбъект.Дата = ТекущаяДата();
    НовыйДокОбъект.Номер = ""; // Сбросим номер, чтобы присвоился новый при записи, если настроена автонумерация
    // Мы можем установить любые другие реквизиты, например:
    // НовыйДокОбъект.Организация = ПолучитьЗначениеПоУмолчанию("ОсновнаяОрганизация");
    // НовыйДокОбъект.Комментарий = "Документ создан по образцу другого документа";

    // При необходимости, мы можем скопировать данные из исходного документа.
    // Для этого нам сначала нужно получить исходный ДокументОбъект.
    // ИсходныйОбъект = ДокументСсылка.ПолучитьОбъект();
    // Если ИсходныйОбъект <> Неопределено Тогда
    //     НовыйДокОбъект.Организация = ИсходныйОбъект.Организация;
    //     НовыйДокОбъект.Контрагент = ИсходныйОбъект.Контрагент;
    //     // Перенос табличных частей может потребовать отдельной логики,
    //     // например, методом Заполнить() или поэлементным копированием.
    // КонецЕсли;

    // Обязательно записываем документ в базу данных.
    // РежимЗаписиДокумента.Запись - сохраняет документ без проведения.
    // РежимЗаписиДокумента.Проведение - сохраняет и проводит документ.
    Попытка
        НовыйДокОбъект.Записать(РежимЗаписиДокумента.Запись);
        Сообщить("Новый документ типа '" + ИмяТипаДокумента + "' успешно создан и записан. Ссылка: " + НовыйДокОбъект.Ссылка);
    Исключение
        Сообщить("Ошибка при записи нового документа: " + ОписаниеОшибки());
    КонецПопытки;

    // Если требуется провести документ сразу после записи:
    // Попытка
    //     НовыйДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
    //     Сообщить("Новый документ успешно проведен.");
    // Исключение
    //     Сообщить("Ошибка при проведении нового документа: " + ОписаниеОшибки());
    // КонецПопытки;

КонецПроцедуры

Жизненный цикл документа в 1С

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

  1. Создание: Документ может быть создан интерактивно пользователем через интерфейс или программно с помощью метода МенеджерДокумента.СоздатьДокумент(). На этом этапе формируется пустой ДокументОбъект.
  2. Заполнение: После создания ДокументОбъект заполняется данными — реквизитами шапки и табличными частями. Это может быть сделано пользователем вручную или программно, например, путем копирования данных из другого документа или заполнения по определенному алгоритму.
  3. Запись: Когда данные документа заполнены, ДокументОбъект сохраняется в базу данных методом Записать(). При первой записи нового документа ему присваивается уникальный номер (если настроена автонумерация) и формируется уникальная ссылка (ДокументСсылка). Документ становится доступным для других пользователей и функционала системы.
  4. Проведение: Проведение документа — это выполнение им движений по регистрам (бухгалтерским, управленческим, накопления и т.д.), что отражает хозяйственную операцию в учете. Проведение обычно выполняется после записи документа методом Записать(РежимЗаписиДокумента.Проведение). Проведенный документ влияет на остатки и обороты в регистрах.
  5. Изменение/Корректировка: Записанный и проведенный документ может быть изменен. При изменении проведенного документа обычно требуется его перепроведение, чтобы движения по регистрам были актуализированы в соответствии с новыми данными.
  6. Отмена проведения: Если необходимо "откатить" влияние документа на учет, его можно отменить проведение. Это убирает все движения, созданные этим документом. После отмены проведения документ считается "непроведенным".
  7. Удаление: Документ может быть помечен на удаление (это обратимая операция, документ остается в базе, но помечается как подлежащий удалению) или непосредственно удален из базы данных (безвозвратно) с помощью специальной обработки "Удаление помеченных объектов" или программно.

Другие способы создания и получения документов

Помимо рассмотренного основного метода, существуют и другие важные подходы к работе с документами, которые мы также рассмотрим.

Получение существующего ДокументОбъекта из ДокументСсылки: метод ПолучитьОбъект()

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


&НаСервере
Процедура ПолучитьСуществующийДокумент(ДокументСсылка)
    Если ДокументСсылка.Пустая() Тогда
        Сообщить("Ссылка пустая, объект получить невозможно.");
        Возврат;
    КонецЕсли;

    // Получаем ДокументОбъект из ДокументСсылки
    ДокОбъект = ДокументСсылка.ПолучитьОбъект();

    Если ДокОбъект <> Неопределено Тогда
        Сообщить("Объект документа '" + ДокОбъект.ПредставлениеДокумента + "' успешно получен.");
        Сообщить("Номер: " + ДокОбъект.Номер + ", Дата: " + ДокОбъект.Дата);
        // Теперь мы можем работать с реквизитами ДокОбъекта.
        // Например, изменить его комментарий и записать:
        // ДокОбъект.Комментарий = "Изменено программно";
        // ДокОбъект.Записать();
    Иначе
        Сообщить("Не удалось получить объект по ссылке. Возможно, документ удален или не существует.");
    КонецЕсли;
КонецПроцедуры

Помните, что ПолучитьОбъект() предназначен для работы с УЖЕ СУЩЕСТВУЮЩИМИ документами, а не для создания новых.

Создание нового документа конкретного типа "с нуля"

Если мы точно знаем тип документа, который хотим создать, и у нас нет исходной ссылки для определения типа, мы можем создать его напрямую через менеджер документов:


&НаСервере
Процедура СоздатьНовыйДокументСНуля()
    // Предположим, мы хотим создать новый документ "РеализацияТоваровУслуг"
    // Мы напрямую обращаемся к менеджеру документов для этого типа
    НовыйДокумент = Документы.РеализацияТоваровУслуг.СоздатьДокумент();

    // Заполняем необходимые реквизиты.
    // В зависимости от конфигурации и бизнес-логики, многие поля могут быть обязательными.
    НовыйДокумент.Дата = ТекущаяДата();
    НовыйДокумент.Организация = Справочники.Организации.НайтиПоКоду("000000001"); // Пример получения организации
    НовыйДокумент.Подразделение = Справочники.ПодразделенияОрганизаций.НайтиПоНаименованию("Основное подразделение"); // Пример
    НовыйДокумент.Комментарий = "Новый документ создан программно с нуля.";

    // Заполнение табличных частей, если необходимо:
    // СтрокаТабличнойЧасти = НовыйДокумент.Товары.Добавить();
    // СтрокаТабличнойЧасти.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("00001");
    // СтрокаТабличнойЧасти.Количество = 10;
    // СтрокаТабличнойЧасти.Цена = 100;
    // СтрокаТабличнойЧасти.Сумма = СтрокаТабличнойЧасти.Количество * СтрокаТабличнойЧасти.Цена;

    Попытка
        НовыйДокумент.Записать(РежимЗаписиДокумента.Запись);
        Сообщить("Создан новый документ РеализацияТоваровУслуг. Ссылка: " + НовыйДокумент.Ссылка);
    Исключение
        Сообщить("Ошибка при создании документа с нуля: " + ОписаниеОшибки());
    КонецПоцедуры

Создание документа с заданным УникальнымИдентификатором (GUID)

Иногда требуется создать документ с заранее известным уникальным идентификатором. Это может быть полезно, например, при загрузке данных из внешних систем, где нужно сохранить исходные идентификаторы для синхронизации.


&НаСервере
Процедура СоздатьДокументСЗаданнымGUID()
    СтрокаGUID = "a1b2c3d4-e5f6-7890-1234-567890abcdef"; // Пример строкового представления GUID
    НовыйGUID = Новый УникальныйИдентификатор(СтрокаGUID); // Преобразуем строку в объект УникальныйИдентификатор

    // Сначала получаем ссылку, которую должен будет иметь новый документ,
    // используя менеджер документов и заданный GUID.
    СсылкаСЗаданнымGUID = Документы.РеализацияТоваровУслуг.ПолучитьСсылку(НовыйGUID);

    // Создаем новый объект документа стандартным способом
    НовыйДокумент = Документы.РеализацияТоваровУслуг.СоздатьДокумент();

    // Устанавливаем ссылку для нового документа перед записью.
    // Метод УстановитьСсылкуНового() используется только для новых,
    // еще не записанных объектов и позволяет присвоить им предопределенную ссылку.
    НовыйДокумент.УстановитьСсылкуНового(СсылкаСЗаданнымGUID);

    НовыйДокумент.Дата = ТекущаяДата();
    НовыйДокумент.Комментарий = "Документ создан с предварительно заданным GUID.";
    // ... заполнение других реквизитов

    Попытка
        НовыйДокумент.Записать(РежимЗаписиДокумента.Запись);
        Сообщить("Документ создан с заданным GUID. Ссылка: " + НовыйДокумент.Ссылка);
    Исключение
        Сообщить("Ошибка при создании документа с заданным GUID: " + ОписаниеОшибки());
    КонецПоцедуры

Важно: Будьте осторожны при ручном задании GUID. Если GUID уже существует в базе данных для другого объекта того же типа, это приведет к ошибке при записи.

Копирование существующего документа: метод Скопировать()

Если наша задача — создать новый документ, который будет очень похож на уже существующий (например, почти все реквизиты должны быть такими же), наиболее эффективным будет использование метода Скопировать(). Этот метод создает новый ДокументОбъект, который является полной копией исходного документа.


&НаСервере
Процедура СкопироватьДокумент(ИсходнаяСсылкаНаДокумент)
    Если ИсходнаяСсылкаНаДокумент.Пустая() Тогда
        Сообщить("Исходная ссылка пустая, копирование невозможно.");
        Возврат;
    КонецЕсли;

    Попытка
        // Метод Скопировать() вызывается у ДокументСсылки и возвращает
        // новый ДокументОбъект, заполненный данными из исходного документа.
        // Этот новый объект еще не записан в базу.
        НовыйОбъект = ИсходнаяСсылкаНаДокумент.Скопировать();

        // Мы можем изменить любые данные в новом объекте до его записи.
        // Например, сбросить номер и установить текущую дату.
        НовыйОбъект.Дата = ТекущаяДата();
        НовыйОбъект.Номер = ""; // Сбросим номер, чтобы система присвоила новый при записи
        НовыйОбъект.Комментарий = "Копия документа: " + ИсходнаяСсылкаНаДокумент.ПредставлениеДокумента;

        НовыйОбъект.Записать(РежимЗаписиДокумента.Запись);
        Сообщить("Документ успешно скопирован. Новая ссылка: " + НовыйОбъект.Ссылка);
    Исключение
        Сообщить("Ошибка при копировании документа: " + ОписаниеОшибки());
    КонецПоцедуры

Использование Скопировать() значительно упрощает создание однотипных документов и уменьшает объем кода, который нужно писать для заполнения реквизитов. Для более сложных сценариев пакетной генерации подойдёт готовый алгоритм создания нескольких документов на основании одного.

Поиск документа по номеру: метод НайтиПоНомеру()

Метод НайтиПоНомеру() менеджера документа позволяет найти ссылку на документ по его номеру. Этот метод вызывается у менеджера конкретного вида документа.


&НаСервере
Процедура ПоискДокументаПоНомеру(НомерДокументаДляПоиска)
    // Для примера ищем документ типа "ПоступлениеТоваровУслуг"
    Менеджер = Документы.ПоступлениеТоваровУслуг;

    // Важно: Если для вида документа в конфигураторе задана периодическая нумерация
    // (например, в пределах года, месяца или дня), то для однозначного поиска
    // необходимо передавать вторым параметром дату, определяющую период.
    // Например, чтобы найти документ с номером "00000001" за текущий год:
    // СсылкаНайденногоДокумента = Менеджер.НайтиПоНомеру(НомерДокументаДляПоиска, НачалоГода(ТекущаяДата()));

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

    Если Не СсылкаНайденногоДокумента.Пустая() Тогда
        Сообщить("Найден документ: " + СсылкаНайденногоДокумента.ПредставлениеДокумента);
    Иначе
        Сообщить("Документ с номером '" + НомерДокументаДляПоиска + "' не найден.");
    КонецЕсли;
КонецПроцедуры

Всегда учитывайте настройки нумерации в конфигураторе, чтобы избежать ошибок при поиске по номеру.

Мы рассмотрели различные подходы к созданию и получению документов в 1С:Предприятии 8.3. Понимание различий между ДокументСсылка, ДокументОбъект и МенеджерДокумента, а также знание соответствующих методов, позволит нам эффективно и корректно работать с данными в системе.

← На главную