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