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