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