При разработке обработок загрузки данных, интеграций или автоматизированных рабочих мест часто возникает задача программного создания документов. Несмотря на кажущуюся простоту, этот процесс имеет множество нюансов: от правильного заполнения реквизитов до корректной обработки ошибок при проведении. В этой статье мы подробно рассмотрим, как правильно работать с объектами документов, заполнять их табличные части и управлять режимами записи.
Разберем задачу по шагам, создавая ощущение, что мы вместе пишем надежный алгоритм для создания документа (например, "Реализация товаров и услуг"). Мы будем использовать методы платформы 1С:Предприятие 8.3 и обратим внимание на важные детали, найденные в документации и практике.
Первый шаг — это получение экземпляра объекта, с которым мы будем работать. В 1С для этого используется менеджер документа. Рассмотрим, как создать новый пустой документ.
Для создания нового документа мы используем метод менеджера СоздатьДокумент(). Этот метод создает объект в оперативной памяти, но еще не записывает его в базу данных. Посмотрим на базовый пример:
// Создаем новый объект документа РеализацияТоваровУслуг
НовыйДокумент = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
Важно: Если нам нужно не создать новый, а найти и изменить существующий документ, мы должны использовать метод ПолучитьОбъект() на ссылке документа. Однако в рамках нашей задачи мы сосредоточимся на создании "с нуля".
После создания объекта нам необходимо заполнить основные реквизиты: дату, организацию, контрагента и склад. Давайте проанализируем, как это делается.
Дата документа является критически важным полем. Если мы не заполним её явно, платформа подставит пустую дату или начало текущей сессии (в зависимости от контекста), что может привести к ошибкам. Рассмотрим пример заполнения шапки:
// Устанавливаем дату и время документа
НовыйДокумент.Дата = ТекущаяДата();
// Заполняем основные реквизиты ссылочными типами
НовыйДокумент.Организация = Справочники.Организации.НайтиПоНаименованию("Основная фирма");
НовыйДокумент.Контрагент = Справочники.Контрагенты.НайтиПоКоду("0000123");
НовыйДокумент.Склад = Справочники.Склады.НайтиПоНаименованию("Основной склад");
// Заполняем текстовые поля и комментарии
НовыйДокумент.Комментарий = "Создано программно: " + Строка(ТекущаяДата());
Обратите внимание на методы поиска ссылок (НайтиПоНаименованию, НайтиПоКоду). В реальных высоконагруженных задачах лучше избегать поиска по наименованию внутри транзакций, так как это может работать медленно или неточно. Рекомендуется использовать заранее подготовленные ссылки или поиск по уникальным идентификаторам (GUID).
Теперь перейдем к более сложной части — заполнению списка товаров. У документа есть табличная часть, которая обычно называется Товары. Нам нужно добавить в неё новые строки.
Для добавления строки используем метод Добавить() на коллекции строк табличной части. Этот метод возвращает объект строки, свойства которой мы затем заполняем. Разберем это на примере:
// Добавляем новую строку в табличную часть Товары
НоваяСтрока = НовыйДокумент.Товары.Добавить();
// Заполняем данные строки
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("Арт-001");
НоваяСтрока.Количество = 10;
НоваяСтрока.Цена = 1500;
НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена;
// Если в документе есть автоматический пересчет (например, НДС),
// иногда требуется вызывать экспортные процедуры модуля объекта,
// но при программном создании часто надежнее рассчитать все явно.
НоваяСтрока.СтавкаНДС = Перечисления.СтавкиНДС.НДС20;
НоваяСтрока.СуммаНДС = НоваяСтрока.Сумма * 20 / 120;
Если у вас есть готовая таблица значений с данными, которые полностью совпадают по именам колонок с табличной частью документа, можно использовать метод Загрузить(). Это значительно ускоряет код и делает его компактнее. Посмотрим, как это выглядит:
// Предположим, ТЗ_Данные - это подготовленная таблица значений
НовыйДокумент.Товары.Загрузить(ТЗ_Данные);
Самый ответственный момент — сохранение изменений в базе данных. Здесь у нас есть два пути: просто записать документ (он останется непроведенным, "черновиком") или провести его (сформировать движения по регистрам).
Для управления этим процессом используется метод Записать(), который принимает параметр типа РежимЗаписиДокумента. Рассмотрим варианты:
РежимЗаписиДокумента.Запись. Документ сохраняется, но не влияет на остатки.РежимЗаписиДокумента.Проведение. Документ сохраняется и делает движения.РежимЗаписиДокумента.ОтменаПроведения. Если документ был проведен, движения очищаются.Пример проведения документа:
Попытка
// Пытаемся записать и провести документ
НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);
Сообщить("Документ успешно проведен: " + НовыйДокумент.Ссылка);
Исключение
// Если возникли ошибки (нехватка остатков, незаполненные поля)
Сообщить("Ошибка при проведении: " + ОписаниеОшибки());
КонецПопытки;
Проанализируем несколько продвинутых моментов, которые отличают код опытного программиста от новичка. Эти знания мы почерпнули из анализа дополнительных источников и документации.
Если вы создаете тысячи документов или загружаете исторические данные, вам часто не нужны все проверки, которые система выполняет при обычной работе пользователя (проверка остатков на тот момент, сложные валидации). Для этого существует свойство ОбменДанными.Загрузка.
Установив это свойство в Истина, мы говорим платформе: "Я знаю, что делаю, запиши данные как есть, без лишних проверок". Однако будьте осторожны: движения по регистрам в этом случае тоже могут не сформироваться автоматически, если логика проведения завязана на это свойство.
НовыйДокумент.ОбменДанными.Загрузка = Истина;
НовыйДокумент.Записать(РежимЗаписиДокумента.Запись);
// При таком режиме обычно используют просто Запись,
// а движения формируют отдельно или полагаются на модуль объекта.
При массовом создании документов крайне важно использовать транзакции. Это гарантирует целостность данных: либо создадутся все документы в пакете, либо ни одного (в случае сбоя). Рассмотрим структуру:
НачатьТранзакцию();
Попытка
// Цикл создания документов
Пока Выборка.Следующий() Цикл
// ... код создания ...
НовыйДокумент.Записать();
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
Сообщить("Произошла ошибка, все изменения отменены. " + ОписаниеОшибки());
КонецПопытки;
При программном создании часто возникает проблема, когда документы создаются в 00:00:00, если указать только дату. Чтобы документы выстраивались в хронологическом порядке, используйте метод УстановитьВремя() или явно задавайте время в поле Дата. Для новых документов также полезен метод УстановитьНовыйНомер(), если автонумерация не срабатывает автоматически так, как вам нужно (обычно она срабатывает в момент записи).
НовыйДокумент.УстановитьНовыйНомер(); // Генерирует код/номер по префиксу организации
Мы рассмотрели полный цикл программного создания документа в 1С. Давайте закрепим основные этапы:
Документы.ИмяВида.СоздатьДокумент().Добавить() или Загрузить().Попытка...Исключение для обработки ошибок.РежимЗаписиДокумента.Используя эти методы, вы сможете создавать надежные алгоритмы обработки данных, которые корректно взаимодействуют с логикой конфигурации.