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