При программном создании документов в 1С (например, при загрузке данных из XML или Excel) разработчики часто сталкиваются с проблемой: общие модули типовых конфигураций, отвечающие за логику заполнения реквизитов (например, счетов учета в "Реализации товаров и услуг"), требуют в качестве параметров данные формы. Конкретно речь идет о типе ДанныеФормыСтруктура. Однако в коде загрузки, который выполняется на сервере, у нас на руках имеется только ДокументОбъект. Попытка передать объект напрямую в процедуру, ожидающую данные формы, приводит к ошибке несоответствия типов.
В этой статье мы подробно разберем, почему возникает эта проблема, и рассмотрим несколько способов её решения: от использования встроенных механизмов БСП до эмуляции данных формы.
Проанализируем архитектуру управляемого приложения. Существует четкое разделение на объектные данные (тип ДокументОбъект, СправочникОбъект), предназначенные для записи в базу данных, и данные формы (тип ДанныеФормыСтруктура, ДанныеФормыКоллекция), предназначенные для отображения в интерфейсе и интерактивного взаимодействия с пользователем.
Многие процедуры в модулях с суффиксами КлиентСервер или Формы написаны специально для работы с экранными формами. Они обращаются к реквизитам через точку (например, Объект.Организация), и платформа позволяет им одинаково работать с ДанныеФормыСтруктура. Но когда мы пытаемся подставить туда реальный ДокументОбъект, код может "споткнуться" на специфических методах, доступных только в данных формы, или на жесткой проверке типов.
Прежде чем пытаться "превратить" объект в форму, выясним, нет ли в конфигурации чисто серверного метода для решения вашей задачи. В современных версиях 1С:Бухгалтерии (БП 3.0) и других типовых решениях на базе БСП для заполнения счетов учета предусмотрены специальные модули, которые работают напрямую с объектами.
Рассмотрим использование программного интерфейса модуля СчетаУчетаВДокументах. Этот способ является наиболее правильным, так как он не требует создания форм и работает максимально быстро.
// Пример программного заполнения счетов учета в объекте документа
// ДокОбъект - это созданный вами ДокументОбъект.РеализацияТоваровУслуг
ПараметрыЗаполнения = Новый Структура;
// При необходимости в структуру можно добавить дополнительные параметры
СчетаУчетаВДокументах.Заполнить(ДокОбъект, ПараметрыЗаполнения);
Этот метод автоматически переберет все табличные части документа (Товары, Услуги и т.д.) и заполнит счета учета согласно настроенным правилам в регистре сведений "Счета учета номенклатуры".
Если вам все же необходимо вызвать конкретную процедуру, которая жестко требует параметр типа ДанныеФормыСтруктура, а переписывать её под объект нельзя, мы можем применить "хитрость". Поскольку обращение к свойствам структуры в языке 1С происходит так же, как и к свойствам объекта формы (через точку), мы можем подменить один тип другим.
Разберем, как подготовить такую структуру для процедуры заполнения счетов учета:
ДанныеОбъекта и заполним её основными реквизитами документа (Организация, Склад, Дата).ДанныеСтроки, содержащую номенклатуру и другие значимые поля.
// Эмуляция данных для вызова процедур формы
ДанныеОбъекта = Новый Структура("Организация, Склад, Дата, ВалютаДокумента, УчитыватьНДС");
ЗаполнитьЗначенияСвойств(ДанныеОбъекта, ДокОбъект);
Для Каждого СтрокаТовара Из ДокОбъект.Товары Цикл
ДанныеСтроки = Новый Структура("Номенклатура, СчетУчета, Сумма, СтавкаНДС");
ЗаполнитьЗначенияСвойств(ДанныеСтроки, СтрокаТовара);
// Вызываем процедуру, которая "думает", что работает с формой
// Обратите внимание: вместо ДанныеФормыСтруктура передаем обычную Структуру
РеализацияТоваровУслугФормыКлиентСервер.НачатьЗаполнениеСчетовУчета(
"Товары.Номенклатура",
Неопределено, // Здесь обычно передается Элементы.Товары
СтрокаТовара,
ДанныеОбъекта,
ДанныеСтроки
);
// Возвращаем измененные данные обратно в строку объекта
ЗаполнитьЗначенияСвойств(СтрокаТовара, ДанныеСтроки);
КонецЦикла;
Важный нюанс: Этот способ сработает только в том случае, если вызываемая процедура не использует специфические методы данных формы, такие как ПроверитьЗаполнение() или прямое обращение к элементам интерфейса через Элементы.
Если ваша логика загрузки реализована внутри управляемой формы внешней обработки, вы можете воспользоваться встроенными функциями платформы для преобразования типов. Вы не можете создать тип ДанныеФормыСтруктура конструктором Новый, но он автоматически создается для реквизитов формы.
Проанализируем алгоритм:
ОбъектДокумента) и типом ДокументОбъект.РеализацияТоваровУслуг.ЗначениеВДанныеФормы().ДанныеФормыСтруктура, и его можно передавать в любые стандартные процедуры.
&НаСервере
Процедура ЗаполнитьЧерезРеквизитПосредник(НовыйДокументОбъект)
// Преобразуем ДокументОбъект в реквизит формы обработки
// "ОбъектДокумента" - это реквизит формы с типом ДокументОбъект.Реализация...
ЗначениеВДанныеФормы(НовыйДокументОбъект, ОбъектДокумента);
// Теперь переменная "ОбъектДокумента" имеет тип ДанныеФормыСтруктура
// Мы можем передавать её в процедуры общего модуля
Для Каждого Строка Из ОбъектДокумента.Товары Цикл
// Вызов типовой логики
ПараметрыЗаполнения = РеализацияТоваровУслугФормыКлиентСервер.ПараметрыЗаполненияСчетовУчета();
// ... заполнение параметров ...
КонецЦикла;
// После выполнения всех манипуляций возвращаем данные обратно в объект
ДанныеФормыВЗначение(ОбъектДокумента, Тип("ДокументОбъект.РеализацияТоваровУслуг"));
КонецПроцедуры
Иногда логика настолько сильно завязана на контекст формы, что без самой формы не обойтись. Этот метод является самым ресурсоемким и "тяжелым", поэтому используйте его только в крайнем случае, когда другие варианты не сработали.
Выясним, как это работает по шагам:
ПолучитьФорму().Объект (который и является ДанныеФормыСтруктура).
&НаКлиенте
Процедура СоздатьИЗаполнитьЧерезФорму()
// Получаем форму нового документа
ПараметрыФормы = Новый Структура;
ФормаДокумента = ПолучитьФорму("Документ.РеализацияТоваровУслуг.ФормаОбъекта", ПараметрыФормы);
// Получаем доступ к ДанныеФормыСтруктура через реквизит формы
ДанныеОбъекта = ФормаДокумента.Объект;
// Выполняем действия на сервере, передавая объект формы
ОбработатьДанныеНаСервере(ДанныеОбъекта);
// Если нужно сохранить
ФормаДокумента.Записать();
КонецПроцедуры
Однако стоит помнить о важном ограничении: передать клиентскую форму целиком на сервер невозможно. Вы передаете только данные (реквизит Объект). Если процедура внутри формы помечена как &НаСервере, вы сможете вызвать её только в том случае, если она объявлена как Экспортная.
Рассмотрев все варианты, мы можем сделать следующие выводы:
Во-первых, всегда ищите возможность использовать чисто серверные методы. В типовых конфигурациях 1С функции заполнения дублируются: одни предназначены для интерактивной работы в форме (быстрые, клиент-серверные), другие — для тяжелых серверных операций (обработка объекта целиком). Для загрузки данных идеальным выбором будет СчетаУчетаВДокументах.Заполнить().
Во-вторых, если вам нужно имитировать ДанныеФормыСтруктура в фоновом задании (где интерфейса нет в принципе), используйте эмуляцию через Структуру. Это самый легкий и быстрый способ обмануть типизацию, если код модуля написан достаточно универсально.
В-третьих, помните о методах ЗначениеВДанныеФормы и ДанныеФормыВЗначение. Они являются мостом между миром объектов базы данных и миром экранных форм. Использование реквизита-посредника в вашей обработке — это надежный способ получить нужный тип данных легальными средствами платформы 1С.
Проанализировав эти подходы, вы сможете эффективно создавать документы программно, не нарушая логику типовых конфигураций и сохраняя корректность заполнения всех реквизитов, включая счета учета и аналитику.