Как программно записать документ 1С через форму и избежать ошибок закрытия?

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

В практике разработки на платформе «1С:Предприятие 8.3» (особенно в конфигурациях «1С:Зарплата и управление персоналом 3.1» и «1С:ERP») программисты часто сталкиваются с ситуацией, когда стандартные бизнес-расчеты жестко привязаны к экранной форме документа. Если попытаться заполнить и записать документ стандартным программным способом на сервере, он может записаться некорректно — без перерасчета налогов, взносов или зависимых показателей.

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

Почему разработчики ЗУП 3.1 вынуждены работать через форму?

Основной корень проблемы кроется в архитектуре типовых решений. Значительная часть сложной расчетной логики (например, автоматическое заполнение табличных частей, расчет НДФЛ при изменении начислений, проверка кадровых условий) реализована не в модуле объекта документа, а в модуле управляемой формы.

Если мы просто создадим или получим объект документа на сервере и изменим его табличную часть, то при вызове метода Записать() вся эта расчетная магия не сработает. Документ останется «недосчитанным». Чтобы запустить алгоритмы перерасчета, разработчики вынуждены программно получать форму, вызывать ее экспортные методы (такие как ПерезаполнитьНачисленияСотрудника) и инициировать запись через контекст формы.

Анализ проблемы: почему возникает ошибка закрытия формы?

Рассмотрим классический пример кода, который часто используется для автоматического перерасчета документов (например, отпусков) из внешней обработки:


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

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

В типовых конфигурациях в обработчиках событий (например, ПослеЗаписи или ПриЗаписиНаСервере) часто прописана логика автоматического закрытия формы после успешной записи с использованием метода ЭтаФорма.Закрыть(). Поскольку форма физически не была открыта на клиенте, вызов метода закрытия приводит к аварийному завершению работы кода с ошибкой: «Операция не доступна для не открытой формы».

Критика «пустой» конструкции Попытка — Исключение

В качестве быстрого решения на форумах часто предлагают обернуть вызов записи в пустую заглушку:


Попытка
    Форма.Записать(ПараметрыЗаписи);
Исключение
    // Пусто: игнорируем ошибку закрытия неоткрытой формы
КонецПопытки;

Внимание: это крайне опасный антипаттерн! Конструкция Попытка - Исключение в данном случае заглушит не только ошибку интерфейса, но и любые критические ошибки базы данных.

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

Рассмотрим правильные и безопасные варианты решения этой проблемы.

Вариант А. Подавление интерактивных вопросов и автозакрытия формы

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


ПараметрыЗаписи = Новый Структура;
ПараметрыЗаписи.Вставить("РежимЗаписи", РежимЗаписиДокумента.Проведение);

// Отключаем вывод стандартных диалогов и вопросов пользователю
ПараметрыЗаписи.Вставить("ВопросыЗарегистрированы", Истина); 

// Указываем системе, что форму закрывать не требуется
ПараметрыЗаписи.Вставить("ЗакрыватьПослеЗаписи", Ложь); 

Форма.Записать(ПараметрыЗаписи);

Также перед выполнением записи рекомендуется принудительно сбросить признак модифицированности формы, чтобы система не пыталась задавать интерактивные вопросы:

Форма.Модифицированность = Ложь;

Вариант Б. Использование «Данных Формы» на Сервере (Рекомендуемый способ)

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

Разберем этот алгоритм по шагам:

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

Посмотрим на практическую реализацию этого метода:


// НА КЛИЕНТЕ:
&НаКлиенте
Процедура ПерерасчитатьДокументБезОткрытия()
    ПараметрыОтбора = Новый Структура("Ключ", Объект.ДокументСсылка);
    Форма = ПолучитьФорму("Документ.НачислениеЗарплаты.ФормаОбъекта", ПараметрыОтбора);
    
    // Получаем структуру данных формы
    ДанныеФормы = Форма.Объект;
    
    // Передаем данные на сервер для обработки и записи
    ОбработатьИЗаписатьНаСервере(ДанныеФормы);
    
    // Обновляем данные в нашей клиентской форме
    КопироватьДанныеФормы(ДанныеФормы, Форма.Объект);
КонецПроцедуры

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

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

Вариант В. Использование программного API конфигурации (Золотой стандарт)

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

Разработчики фирмы 1С активно выносят всю сложную бизнес-логику расчетов в специализированные общие модули. Чтобы программно пересчитать и провести документ без обращения к формам, мы должны использовать именно этот программный интерфейс (API).

Проанализируем пример схемы кода для полной автоматизации расчета в ЗУП 3.1:


&НаСервере
Процедура ПрограммныйПерерасчетНаСервере(ДокументСсылка)
    // 1. Получаем объект документа
    ДокОбъект = ДокументСсылка.ПолучитьОбъект();
    
    // 2. Очищаем или подготавливаем необходимые таблицы
    ДокОбъект.Начисления.Очистить();
    
    // 3. Используем функции программного интерфейса ЗУП для заполнения и расчета
    ПараметрыРасчета = РасчетЗарплатыРасширенный.ПараметрыЗаполненияДокумента(ДокОбъект);
    РасчетЗарплатыРасширенный.ЗаполнитьДокумент(ДокОбъект, ПараметрыРасчета);
    
    // 4. Записываем и проводим документ
    ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
КонецПроцедуры

Использование общего модуля РасчетЗарплатыРасширенный или аналогичных API-модулей конфигурации гарантирует, что все зависимые расчеты (НДФЛ, страховые взносы, удержания) будут выполнены корректно, а выполнение кода не будет зависеть от логики визуального интерфейса.

← На главную