Как программно создать документ в 1С с автоматическим заполнением реквизитов, как при интерактивном вводе?

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

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

Почему интерактивное создание отличается от программного?

Когда пользователь создает новый документ интерактивно, например, через команду "Создать" в списке документов, платформа выполняет целый ряд действий. Открывается форма документа, и в процессе ее инициализации срабатывают различные обработчики, как на клиенте, так и на сервере. Эти обработчики могут:

  1. Вызывать событие ОбработкаЗаполнения объекта документа. Если документ создается на основании другого документа или с использованием определенных данных заполнения, этот обработчик играет ключевую роль в первоначальном заполнении реквизитов.
  2. Выполнять процедуры в модуле формы. Многие алгоритмы заполнения, пересчета и проверки данных могут быть реализованы непосредственно в модуле формы документа. Эти процедуры могут быть как клиентскими (&НаКлиенте), так и серверными (&НаСервере, &НаСервереБезКонтекста), но они тесно связаны с контекстом открытой формы.
  3. Применять значения заполнения реквизитов. Некоторые реквизиты могут иметь свойства ЗначениеЗаполнения или ЗаполнятьИзДанныхЗаполнения, которые используются платформой при создании нового документа.
  4. Работать с элементами формы. В процессе интерактивного ввода пользователь взаимодействует с элементами формы, и их изменения могут запускать дополнительные обработчики, влияющие на другие реквизиты.

В то же время, при программном создании документа методом Документы.ИмяДокумента.СоздатьДокумент(), мы получаем в памяти объект, который, по сути, является "сырой" заготовкой. Этот объект не имеет связанной формы, и следовательно, никакие обработчики модуля формы или алгоритмы, завязанные на пользовательский интерфейс, не выполняются автоматически. Именно поэтому многие реквизиты остаются пустыми или заполняются значениями по умолчанию, а не теми, которые ожидает увидеть пользователь при интерактивном вводе.

Ключевая проблема: не существует универсального метода Объект.ВыполнитьВсеОбработчикиФормыПриСозданииНовогоДокумента(). Логика заполнения документов часто распределена между модулем объекта, модулем формы и подписками на события, что затрудняет ее централизованное воспроизведение. Более того, обратиться к процедурам модуля формы без явного создания и открытия формы обычно невозможно, а часть логики может быть реализована на клиенте и требовать прямого взаимодействия с элементами интерфейса.

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

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

  1. Использование обработчика ОбработкаЗаполнения

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

    • При интерактивном вводе нового документа.
    • При вводе документа на основании другого.
    • При программном вызове метода Заполнить() объекта документа.

    Если в конфигурации логика первоначального заполнения документа реализована именно в обработчике ОбработкаЗаполнения модуля объекта, то для ее выполнения при программном создании достаточно вызвать метод Заполнить(). В обработчике этого события должен быть описан алгоритм заполнения реквизитов документа на основании переданных данных. Если данные заполнения представляют собой структуру, это означает, что объект создается программно с передачей этой структуры через методы ОткрытьФорму(), ПолучитьФорму() или Заполнить(). Рекомендуется использовать исключение (ВызватьИсключение) для оповещения пользователя о причинах отказа в заполнении, а не пытаться отменить действие через параметр Отказ (которого нет) или СтандартнаяОбработка = Ложь (что не отменяет действие).

    Рассмотрим пример:

    
    // Создаем новый объект документа
    ОбъектДокумента = Документы.ПлатежноеПоручениеВходящее.СоздатьДокумент();
    
    // Устанавливаем базовые данные, которые могут быть использованы в ОбработкаЗаполнения
    // Например, если документ создается на основании, или если передаются параметры
    СтруктураДанныхЗаполнения = Новый Структура();
    СтруктураДанныхЗаполнения.Вставить("Контрагент", СсылкаНаКонтрагента);
    СтруктураДанныхЗаполнения.Вставить("Сумма", 12500);
    СтруктураДанныхЗаполнения.Вставить("Организация", СсылкаНаОрганизацию);
    СтруктураДанныхЗаполнения.Вставить("Валюта", СсылкаНаВалюту);
    
    // Вызываем метод Заполнить(), который инициирует событие ОбработкаЗаполнения
    // и передаем туда структуру с данными
    ОбъектДокумента.Заполнить(СтруктураДанныхЗаполнения);
    
    // После выполнения ОбработкаЗаполнения, реквизиты объекта будут заполнены
    // согласно логике, описанной в этом обработчике.
    // Теперь мы можем дозаполнить специфичные реквизиты, если это необходимо
    // (например, табличные части или реквизиты, которые не были учтены в ОбработкаЗаполнения).
    
    // Пример дозаполнения табличной части, если СтавкаНДС не заполнилась автоматически
    // и требует ручного добавления в расшифровку платежа.
    Если ОбъектДокумента.РасшифровкаПлатежа.Количество() = 0 Тогда
        НоваяСтрока = ОбъектДокумента.РасшифровкаПлатежа.Добавить();
        НоваяСтрока.СтавкаНДС = Перечисления.СтавкиНДС.НДС20; // Или НДС10, БезНДС
        НоваяСтрока.СуммаНДС = 2083.33; // Расчетная сумма НДС (12500 / 1.2 * 0.2)
        НоваяСтрока.Сумма = 12500; // Сумма по строке с учетом НДС
        НоваяСтрока.Содержание = "Оплата за товары";
        НоваяСтрока.СтатьяДвиженияДенежныхСредств = СсылкаНаСтатьюДДС;
    КонецЕсли;
    
    // Записываем документ. Если документ должен быть проведен, можно использовать Провести()
    // или Записать(РежимЗаписиДокумента.Проведение).
    ОбъектДокумента.Записать(РежимЗаписиДокумента.Запись);
    

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

  2. Ручное заполнение реквизитов объекта

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

    Как выяснить, что заполняется?

    1. Интерактивное создание и отладчик: Самый надежный способ — это создать документ интерактивно и проследить за процессом в отладчике. Установите точки останова в обработчиках модуля объекта (ПриСозданииНаСервере, ОбработкаЗаполнения, ПриЗаписи, ПередЗаписью) и, что особенно важно, в модуле формы (ПриСозданииНаСервере, ПриОткрытии, ПриИзменении полей). Это позволит вам понять, какие реквизиты и в какой момент заполняются, а также какие процедуры выполняются.
    2. Анализ типового кода: Внимательно изучите модуль объекта и модуль формы документа. Ищите процедуры и функции, которые отвечают за расчеты, подстановку значений по умолчанию и проверки.
    3. Просмотр подписок на события: В конфигурации могут существовать подписки на события, которые срабатывают при создании, записи или проведении документов. Они также могут изменять или заполнять реквизиты.

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

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

    
    // Предположим, ОбъектДокумента уже создан методом СоздатьДокумент()
    // и основные реквизиты шапки (Контрагент, Организация) уже заполнены.
    
    // Добавляем строки в табличную часть "РасшифровкаПлатежа"
    // Каждая строка здесь может представлять отдельную услугу/товар с своей ставкой НДС.
    НоваяСтрока = ОбъектДокумента.РасшифровкаПлатежа.Добавить();
    НоваяСтрока.НазначениеПлатежа = "Оплата за ноутбук Dell XPS";
    НоваяСтрока.Сумма = 83333.33; // Сумма без НДС
    НоваяСтрока.СтавкаНДС = Перечисления.СтавкиНДС.НДС20;
    НоваяСтрока.СуммаНДС = 16666.67;
    НоваяСтрока.СтатьяДвиженияДенежныхСредств = СсылкаНаСтатьюДДС;
    
    // Если нужна еще одна строка
    НоваяСтрока2 = ОбъектДокумента.РасшифровкаПлатежа.Добавить();
    НоваяСтрока2.НазначениеПлатежа = "Доставка";
    НоваяСтрока2.Сумма = 500;
    НоваяСтрока2.СтавкаНДС = Перечисления.СтавкиНДС.НДС20;
    НоваяСтрока2.СуммаНДС = 100;
    НоваяСтрока2.СтатьяДвиженияДенежныхСредств = СсылкаНаСтатьюДДС;
    
    // После заполнения всех реквизитов и табличных частей
    // Выполняем запись документа
    ОбъектДокумента.Записать(РежимЗаписиДокумента.Запись);
    

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

  3. Работа с формной логикой (обходные пути)

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

    Один из таких обходных путей, хотя и не рекомендованный для фонового создания документов из-за накладных расходов и потенциальных проблем с контекстом, заключается в программном открытии формы документа, вызове необходимых экспортных процедур/функций из ее модуля, и последующем получении результатов.

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

    
    // Шаг 1: Создаем и записываем пустой документ, чтобы получить ссылку
    // Это нужно, потому что форма открывается для существующего объекта.
    Док = Документы.ПлатежноеПоручениеВходящее.СоздатьДокумент();
    Док.Контрагент = СсылкаНаКонтрагента;
    Док.Сумма = 10000;
    Док.Организация = СсылкаНаОрганизацию;
    Док.Валюта = СсылкаНаВалюту;
    // Если у нового документа есть основание, то в обработке заполнения можно подсмотреть,
    // как интерактивно создается новый документ.
    // Также можно использовать свойства реквизитов документа ЗначениеЗаполнения, ЗаполнятьИзДанныхЗаполнения
    // или обработчик события документа ОбработкаЗаполнения.
    Док.Записать(); // Записываем, чтобы получить ссылку
    
    // Шаг 2: Получаем форму документа по ссылке
    ФормаДокумента = Док.Ссылка.ПолучитьФорму("ФормаДокумента");
    // Если логика заполнения находится в модуле формы, иногда возникает необходимость
    // вызвать серверные процедуры из клиентского контекста.
    // Это можно сделать, описав серверные процедуры с директивами компиляции &НаСервере
    // или &НаСервереБезКонтекста в модуле формы или в общем модуле.
    
    // Шаг 3: Вызываем экспортную процедуру/функцию из модуля формы
    // (Предполагается, что в модуле формы есть экспортная процедура,
    // например, "РассчитатьНДСПоТабличнойЧастиНаСервере" или "ПолучитьСтавкуНДСПоУмолчанию")
    Если ФормаДокумента.Модифицированность Тогда // Если что-то изменилось в форме при открытии
        ФормаДокумента.Записать(); // Сохраняем изменения, если форма их внесла (в объект, который еще не записан)
    КонецЕсли;
    
    // Если нам нужно получить значение из формы:
    // Например, если на форме есть функция ЭкспортнаяФункцияРасчетаНДС()
    Попытка
        // Вызов экспортной процедуры из формы для выполнения расчетов
        // Например, если на форме есть процедура РассчитатьТабличнуюЧастьНаСервере()
        ФормаДокумента.РассчитатьТабличнуюЧастьНаСервере();
    
        // Получаем обновленный объект после работы формы
        ОбъектДокумента = Док.ПолучитьОбъект(); 
        
        // Теперь объект должен содержать реквизиты, заполненные логикой формы
        // Можно провести дополнительные проверки или дозаполнения
        Если ОбъектДокумента.РасшифровкаПлатежа.Количество() > 0 Тогда
            Сообщить("Табличная часть заполнена логикой формы.");
            // Пример: Получим ставку НДС из первой строки, если она заполнена
            СтавкаНДСИзФормы = ОбъектДокумента.РасшифровкаПлатежа[0].СтавкаНДС;
        КонецЕсли;
    
        ОбъектДокумента.Записать(РежимЗаписиДокумента.Запись);
    
    Исключение
        Сообщить("Не удалось вызвать метод из формы: " + ОписаниеОшибки());
    КонецПопытки;
    
    // Шаг 4: Закрываем форму, если она была открыта только для вызова алгоритма
    // Это важно для освобождения ресурсов.
    ФормаДокумента.Закрыть();
    
    // После программного создания и заполнения документа, можно открыть его форму для пользователя
    // с помощью метода ОткрытьЗначение(СсылкаНаДокумент).
    

    ВНИМАНИЕ: Этот подход следует использовать очень осторожно и только в крайних случаях, поскольку он:

    • Требует существования экспортных процедур в модуле формы.
    • Имеет значительные накладные расходы, так как фактически происходит создание и инициализация интерфейсного объекта.
    • Может вызывать проблемы в фоновых заданиях или на сервере, где отсутствует пользовательский интерфейс.
    • Не гарантирует стабильности при обновлениях, так как логика формы может измениться.

    Всегда предпочтительнее переносить бизнес-логику в модуль объекта или общие модули. Если требуется предварительное заполнение, можно использовать свойства реквизитов документа ЗначениеЗаполнения, ЗаполнятьИзДанныхЗаполнения или обработчик события документа ОбработкаЗаполнения.

  4. Комплексный подход: совмещение методов

    На практике наиболее эффективным часто оказывается комбинированный подход:

    1. Начните с вызова ОбъектДокумента.Заполнить(ПараметрыЗаполнения), чтобы задействовать логику обработчика ОбработкаЗаполнения.
    2. После этого вручную заполните оставшиеся реквизиты и табличные части, которые не были заполнены автоматически. Для этого используйте данные, полученные из внешних источников или рассчитанные по определенным правилам.
    3. При необходимости вызовите экспортные процедуры из общих модулей, которые могут содержать общие алгоритмы расчетов или проверок, не привязанные к конкретной форме.

    Этот подход позволяет использовать существующую типовую логику там, где это возможно, и дополнять ее собственным кодом для тех случаев, где автоматическое заполнение отсутствует или недостаточно.

  5. Использование подписок на события и общих модулей

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

    Всегда полезно провести поиск по имени реквизита (например, "СтавкаНДС") по всей конфигурации, чтобы определить все места, где этот реквизит заполняется или изменяется. Это даст полное представление о его жизненном цикле.

  6. Особенности заполнения в типовых конфигурациях

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

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

    В типовых конфигурациях могут быть предусмотрены механизмы автозаполнения по шаблонам (например, в 1С:Документооборот, 1С:ERP Управление холдингом), которые позволяют заполнять реквизиты документа из других документов или справочников. Также могут использоваться внешние обработки вида "Заполнение объекта" для загрузки данных из файлов или других источников, которые могут открывать форму документа и передавать ей контекст вызывающей формы для манипуляций с данными.

    Поэтому при работе с типовыми конфигурациями крайне важно максимально использовать стандартные механизмы (ОбработкаЗаполнения, типовые экспортные функции) и минимизировать дублирование кода.

Рекомендации и лучшие практики

Для успешного программного создания и заполнения документов в 1С рекомендуем придерживаться следующих принципов:

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

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

← На главную