При работе с COM-соединением в 1С 8.3 часто возникают вопросы о доступе к реквизитам объектов конфигурации. В особенности, это касается объекта "Обработка", который имеет свою специфику. Мы выясним, как правильно работать с реквизитами обработок через COM, чтобы избежать типичных ошибок и эффективно решать поставленные задачи.
Предположим, что у нас уже установлено COM-соединение с базой 1С (например, через COM объект на Python) и объект smol1c_COM_Exemplar является экземпляром COM-объекта 1С (например, COMОбъект("V83.Application") или COMОбъект("V83.COMConnector")).
Прежде чем мы перейдем к техническим деталям, нам критически важно понять фундаментальное отличие объекта "Обработка" от других объектов конфигурации, таких как "Документ", "Справочник" или "РегистрСведений".
Из этого следует, что мы не можем "прочитать" значение реквизита "Обработки" из базы данных, потому что оно там просто не хранится. Мы можем создать экземпляр обработки, установить или прочитать значения ее реквизитов, но эти значения будут актуальны только для данного конкретного запущенного экземпляра.
Если наша цель состоит не в получении текущего значения реквизита в работающей обработке, а в том, чтобы узнать структуру объекта "Обработка", то есть получить список ее реквизитов, их типы и другие метаданные, мы можем использовать объект Метаданные. Этот подход позволяет нам получить информацию о том, какие реквизиты вообще существуют в конфигурации для данной обработки, но не их значения.
Сначала мы получаем доступ к коллекции обработок через объект Метаданные, а затем находим нужную обработку по имени. Например, для обработки "МояОбработка":
var K1: Variant;
K1 := smol1c_COM_Exemplar.Метаданные.Обработки.Найти('МояОбработка');
// K1 теперь содержит объект Метаданные.Обработка
Или напрямую, если имя известно:
var K1: Variant;
K1 := smol1c_COM_Exemplar.Метаданные.Обработки.МояОбработка;
// K1 теперь содержит объект Метаданные.Обработка
После получения объекта метаданных обработки, мы можем получить доступ к коллекции ее реквизитов:
var K1, K2: Variant;
K1 := smol1c_COM_Exemplar.Метаданные.Обработки.Найти('МояОбработка');
K2 := K1.Реквизиты.Найти('Мой_реквизит_число');
// K2 теперь содержит объект Метаданные.РеквизитОбработки
Объект Метаданные.РеквизитОбработки даст нам информацию о самом реквизите (его имя, тип, составной ли тип и т.д.), но не его значение. Например, мы можем узнать имя реквизита:
ShowMessage(smol1c_COM_Exemplar.String(K2.Имя)); // Выведет "Мой_реквизит_число"
Важно: Этот подход полезен для анализа структуры конфигурации, выгрузки метаданных в JSON или динамического формирования запросов, но не для получения значений, которые пользователь мог ввести в форму обработки или которые были установлены в ходе ее выполнения.
Если нам нужно установить или прочитать значения реквизитов конкретного, работающего экземпляра обработки, нам необходимо этот экземпляр создать. Только после создания объекта обработки в памяти мы сможем взаимодействовать с его реквизитами как со свойствами.
Для обработок, входящих в состав конфигурации (встроенных), мы сначала получаем объект-менеджер обработки, а затем вызываем его метод Создать(). Это наиболее распространенный и корректный способ:
var K1: Variant;
K1 := smol1c_COM_Exemplar.Обработки.ЗакрытиеМесяца.Создать();
// K1 теперь является экземпляром ОбработкаОбъект.ЗакрытиеМесяца
Если мы попытаемся получить объект напрямую через smol1c_COM_Exemplar.Обработки.ЗакрытиеМесяца; без .Создать(), то получим не сам объект обработки, а ее менеджер (ОбработкаМенеджер.ЗакрытиеМесяца), который предназначен для работы с метаданными и создания новых объектов.
В некоторых случаях (например, для внешних обработок, или если полный тип объекта доступен), мы можем попытаться создать экземпляр через метод NewObject(), указав полный тип объекта ОбработкаОбъект.<ИмяОбработки>:
var K1: Variant;
// Этот вариант может работать для некоторых сценариев,
// но 'не найден тип' часто возникает для внутренних обработок
K1 := smol1c_COM_Exemplar.NewObject("ОбработкаОбъект.ОбработкаВнутренняЛюбая");
Если мы получаем ошибку "не найден тип" (как это было в изначальном вопросе), это часто означает, что платформа 1С не может найти или корректно распознать указанный тип объекта в данном контексте COM-соединения. В таких случаях метод smol1c_COM_Exemplar.Обработки.ИмяОбработки.Создать() является предпочтительным и более надежным для встроенных обработок.
После того как мы успешно создали экземпляр обработки, мы можем устанавливать значения ее реквизитов как обычные свойства объекта:
var K1: Variant;
K1 := smol1c_COM_Exemplar.Обработки.ЗакрытиеМесяца.Создать();
// Устанавливаем значение реквизита "ПериодРегистрации"
K1.ПериодРегистрации = smol1c_COM_Exemplar.NewObject("Дата", 2023, 12, 31);
// Устанавливаем значение реквизита "Организация"
K1.Организация = smol1c_COM_Exemplar.Справочники.Организации.НайтиПоКоду("001");
Обратите внимание: если реквизит имеет ссылочный тип (например, СправочникСсылка.Организации), нам нужно передавать ему не просто строку с кодом или наименованием, а ссылку на объект 1С, полученную через COM.
Точно так же, как мы устанавливали значения, мы можем их и считывать. Важно помнить, что мы считываем значения, которые были установлены для данного экземпляра обработки:
var K1, ЗначениеПериода: Variant;
K1 := smol1c_COM_Exemplar.Обработки.ЗакрытиеМесяца.Создать();
K1.ПериодРегистрации = smol1c_COM_Exemplar.NewObject("Дата", 2023, 12, 31);
ЗначениеПериода = K1.ПериодРегистрации;
ShowMessage(smol1c_COM_Exemplar.String(ЗначениеПериода));
// Выведет дату, которую мы установили
После заполнения реквизитов, мы можем вызвать методы обработки для выполнения ее функционала. Например, если у обработки есть метод ВыполнитьЗакрытие():
K1.ВыполнитьЗакрытие();
В этом случае, логика обработки будет использовать значения реквизитов, которые мы установили для данного экземпляра.
Таким образом, для работы с реквизитами объекта "Обработка" через COM, мы всегда взаимодействуем с ее экземпляром в памяти, а не с какой-либо хранимой в базе записью.
В некоторых случаях, когда мы пытаемся получить "значение", которое раньше присваивалось реквизиту обработки, это значение на самом деле могло вычисляться в каком-либо общем модуле, а затем использоваться обработкой. Если цель состоит в получении результата работы такой функции, то более прямым и эффективным способом будет вызов этой функции общего модуля напрямую через COM.
Чтобы функция общего модуля была доступна через COM-соединение, сам общий модуль должен иметь установленные свойства:
Без этих флагов, попытка обращения к функциям модуля извне приведет к ошибке.
Если общий модуль соответствует требованиям, мы можем вызвать его функции, используя следующий синтаксис:
var РезультатФункции: Variant;
// Предполагаем, что есть общий модуль "МойОбщийМодуль" с функцией "ПолучитьЧисло"
РезультатФункции := smol1c_COM_Exemplar.МойОбщийМодуль.ПолучитьЧисло(Параметр1, Параметр2);
ShowMessage(smol1c_COM_Exemplar.String(РезультатФункции));
В исходном вопросе был пример:
// Это более сложный случай, где ОбщегоНазначения, возможно, не сам модуль,
// а некий объект, предоставляющий доступ к функционалу,
// как это часто бывает с БСП (Библиотекой Стандартных Подсистем).
K1:=smol1c_COM_Exemplar.ОбщегоНазначения.ОбщийМодуль('ЗакрытиеМесяца');
// Затем, если K1 это объект, который имеет метод или свойство "КлючОбъектаПодсказки"
// ShowMessage(smol1c_COM_Exemplar.String(K1.КлючОбъектаПодсказки));
В стандартном случае без БСП, вызов функции общего модуля выглядит проще: smol1c_COM_Exemplar.ИмяОбщегоМодуля.ИмяФункции(Параметры). Если же используется функционал БСП, как в примере с ОбщегоНазначения.ОбщийМодуль('ЗакрытиеМесяца'), это может быть специфичный механизм получения ссылки на функционал, который работает через специализированные методы БСП.
В файловой базе данных вызов общего модуля через COM обычно работает без дополнительных сложностей (при соблюдении флагов модуля). Однако, в серверной версии 1С могут возникать нюансы с производительностью или правами доступа, если функции общего модуля выполняют сложные операции или требуют особых привилегий.
Если мы все-таки хотим получить значения, которые хранятся в базе данных и лишь каким-то образом связаны с обработкой (например, обработка их создала, изменила или отобразила), то нам следует обращаться не к самой обработке, а к тем объектам, которые эти данные непосредственно хранят.
Для чтения данных из базы данных наиболее мощным и универсальным инструментом является объект Запрос. Мы можем формировать запросы к таблицам документов, справочников, регистров и получать нужные нам поля:
var K1, K1DATA: Variant;
K1 := smol1c_COM_Exemplar.NewObject('Запрос');
K1.Text :=
"ВЫБРАТЬ
| ДокументПоступлениеТоваровУслуг.Ссылка,
| ДокументПоступлениеТоваровУслуг.Дата,
| ДокументПоступлениеТоваровУслуг.Организация
|ИЗ
| Документ.ПоступлениеТоваровУслуг КАК ДокументПоступлениеТоваровУслуг
|ГДЕ
| ДокументПоступлениеТоваровУслуг.Дата МЕЖДУ &НачалоПериода И &КонецПериода";
K1.УстановитьПараметр("НачалоПериода", smol1c_COM_Exemplar.NewObject("Дата", 2023, 1, 1));
K1.УстановитьПараметр("КонецПериода", smol1c_COM_Exemplar.NewObject("Дата", 2023, 12, 31));
K1DATA := K1.Выполнить().Выбрать();
while K1DATA.Следующий() do begin
ShowMessage(smol1c_COM_Exemplar.String(K1DATA.Ссылка) + " | " + smol1c_COM_Exemplar.String(K1DATA.Дата));
end;
Именно этот подход следует использовать, когда мы хотим получить данные, сохраненные в информационной базе.
Если у нас уже есть ссылка на конкретный объект (например, на документ или элемент справочника), мы можем прочитать его реквизиты напрямую:
var СсылкаНаДокумент, ОбъектДокумент, ЗначениеРеквизита: Variant;
// Получаем ссылку на какой-либо документ (например, по UUID)
СсылкаНаДокумент := smol1c_COM_Exemplar.Документы.ПоступлениеТоваровУслуг.ПолучитьСсылку(smol1c_COM_Exemplar.NewObject("УникальныйИдентификатор", "{ваш_uuid_документа}"));
// Получаем объект документа (читаем его из базы)
ОбъектДокумент := СсылкаНаДокумент.ПолучитьОбъект();
// Читаем реквизит
ЗначениеРеквизита := ОбъектДокумент.Организация;
ShowMessage(smol1c_COM_Exemplar.String(ЗначениеРеквизита));
Этот способ позволяет получить все реквизиты объекта, которые были сохранены в базе данных.
Если в конфигурации используется БСП, можно воспользоваться функцией ОбщегоНазначения.ЗначениеРеквизитаОбъекта(). Эта функция позволяет получить значение реквизита, прочитанного из информационной базы по ссылке на объект, без необходимости получать весь объект целиком. Однако она доступна только на сервере 1С.
var СсылкаНаОбъект, ЗначениеРеквизита: Variant;
// Получаем ссылку на объект (например, на Организацию)
СсылкаНаОбъект := smol1c_COM_Exemplar.Справочники.Организации.НайтиПоНаименованию("ООО Рога и Копыта");
// Если функция доступна через COM и имеет нужные флаги
// ЗначениеРеквизита := smol1c_COM_Exemplar.ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаОбъект, "ИНН");
// ShowMessage(smol1c_COM_Exemplar.String(ЗначениеРеквизита));
Для использования этой функции через COM также требуется, чтобы общий модуль ОбщегоНазначения имел флаг "Внешнее соединение".
Мы видим, что доступ к реквизитам объекта конфигурации "Обработка" через COM требует четкого понимания ее природы. Если наша цель — получить описание структуры обработки, мы используем объект Метаданные. Если мы хотим установить или прочитать параметры выполнения конкретного экземпляра обработки, мы создаем этот экземпляр через .Создать() и работаем с его свойствами в памяти. Если же искомое "значение" является результатом вычислений, вынесенных в общий модуль, мы напрямую вызываем функцию этого модуля (при условии, что он доступен для внешнего соединения). Наконец, если нам нужны данные, которые хранятся в базе данных, мы обращаемся к соответствующим объектам конфигурации (документам, справочникам, регистрам) через запросы или прямым чтением реквизитов объекта.
Выбор правильного подхода зависит от конкретной задачи и от того, где именно находится искомая информация.