Предисловие
Если вы когда-либо пробовали реализовывать интеграцию с 1С:Предприятие, то вы, вероятно, уже знаете, что разумных способов это сделать не так много. Вот список механизмов, с которыми вы могли столкнуться:
- механизмы взаимодействия через COM-соединение;
- механизмы web/http - сервисов;
- механизмы обмена XML-пакетами (конвертация данных, EnterpriseData и пр..);
- стандартизированный протокол OData.
Указанные механизмы не являются в общем случае взаимозаменяемыми, каждый из них имеет как преимущества так и недостатки, но независимо от выбора, их использование оказывается достаточно трудоемкой задачей.
Мы решили упростить процесс интеграции с 1С и создали расширение, которое и хотим вам представить.
Возможности расширения
Расширение Бром может быть установлено на любую конфигурацию 1С, которая поддерживает работу с расширениями (версия совместимости 8.3.10 или выше). Расширение не требует наличия стандартных библиотек, поэтому будет работать даже на самописных конфигурациях. Расширение позволяет:
- получать и редактировать данные объектов 1С (ссылочные объекты, константы, параметры сеанса);
- получать выборки из коллекций с учетом сложных условий отбора и сортировки;
- исполнять произвольные запросы на языке 1С, в том числе пакетные запросы и запросы с разметкой для построителя;
- вызывать процедуры и функции 1С с возможностью передачи параметров и получения результата в естественном виде (без необходимости преобразования типов);
- получать данные о метаданных конфигурации с возможностью их частичной (постраничной) загрузки;
- передавать в обоих направлениях данные как примитивных типов, так и сложные структурированные данные (ссылки, массивы, структуры, соответствия, таблицы значений, деревья, системные перечисления и многое другое).
Функциональность позволяет создать в клиентском приложении свою ORM. Вообще-то, мы уже сделали это за вас, но об этом далее.
Принцип работы
Принцип интеграции с 1С следующий:
- Подготавливаем инфраструктуру на стороне 1С:
- В конфигурацию 1С устанавливаем расширение Бром. Расширение содержит веб-сервис, который будет отвечает на клиентские запросы. Также расширение содержит собственный сериализатор, который преобразует данные 1С в XDTO-объекты и обратно.
- Создаем одного или нескольких пользователей, которым доступны роли основной конфигурации и роли расширения (или выдаем эти роли существующим пользователям);
- Публикуем конфигурацию на веб-сервере, чтобы она откликалась по HTTP;
- В клиентском приложении подключаем готовую библиотеку, создаем клиентский объект-коннектор и пользуемся им.
На момент написания данной статьи реализованы клиентские библиотеки для .Net Core, PHP и Python.
Если ваш проект работает на другой платформе, то свою клиентскую библиотеку вы можете реализовать самостоятельно. Это возможно сделать на любом современном языке программирования.
Примеры использования
Теперь давайте посмотрим как выглядит клиентский код. В этой статья я буду использовать C#. Код на PHP и Python полностью аналогичен.
В качестве удаленной конфигурации будет выступать УТ 11, вы можете проводить эксперименты на любой другой.
Подключение и инициализация
Подключаемую библиотеку можно скачать с сайта проекта или репозитория (библиотеки для .Net Core и Python лучше ставить из репозитория, т.к. они имеют зависимости). После подключения библиотеки к проекту нам необходимо создать объект "БромКлиент". Сделать это можно одной командой:
dynamic клиент = new БромКлиент(@"
Публикация = http://domainname.ru/publication_name;
Пользователь = username;
Пароль = userpassword
");
В параметрах подключения достаточно указать адрес нашей публикации, логин и пароль пользователя, которому мы предварительно выдали роли расширения Бром.
Наш бром-клиент готов к использованию. Поехали дальше!
Вызов процедур и функций 1С
Давайте теперь вызовем простейшую функцию 1С, например "ЧислоПрописью". В качестве кода локализации укажем французский язык:
var числоПрописью = клиент.ЧислоПрописью(2547, "Л = fr_FR");
// -> "Cent vingt-trois mille cent vingt-trois 00"
Данная функция принимает и возвращает значения примитивных типов. Давайте вызовем функцию, которая вернет нам массив:
var цвета = клиент.СтрРазделить("Красный,Синий,Зеленый", ",");
// -> Массив ["Красный", "Синий", "Зеленый"]
Теперь давайте вызовем функцию "НайтиПоКоду" модуля менеджера справочника "Валюты":
var доллар = клиент.Справочники.Валюты.НайтиПоКоду(840);
//-> СправочникСсылка (Доллар)
Функция вернула нам ссылку на элемент справочника. Со ссылками можно делать много полезных вещей, но пока что мы просто передадим эту ссылку в качестве параметра в другую функцию.
var данные = клиент.РаботаСКурсамиВалют.ПолучитьКурсВалюты(доллар, DateTime.Today);
//-> Структура
var курс = данные.Курс;
var кратность = данные.Кратность;
В этом примере мы обратились к функции общего модуля, передали в нее ссылку на элемент справочника и дату. Функция вернула нам объект типа "Структура". Обращаться с этим объектом мы можем так же как и в 1С.
Доступность тех или иных методов можно ограничивать с помощью настроек областей видимости в расширении. Так, например, если указать область «Справочники.*.НайтиПоКоду», то будет доступен только метод «НайтиПоКоду» во всех справочниках конфигурации.
Работа со ссылками
Мы уже умеем находить ссылки на объекты коллекций в 1С. Давайте теперь получим данные этих объектов:
var заказ = клиент.Документы.ЗаказКлиента.НайтиПоНомеру("ТД00-000018", new Date(2017, 1, 1));
var датаЗаказа = заказ.Дата;
var контрагент = заказ.Контрагент;
var иннКонтрагента = заказ.Контрагент.ИНН;
foreach (var стр in заказ.Товары) {
Console.WriteLine((стр.Номенклатура, стр.Количество));
}
В этом примере мы получили ссылку на документ, а далее через нее уже получили значения реквизитов и табличной части. При обращении к любому атрибуту ссылки из 1С загружаются все данные объекта, поэтому дальнейшее обращение к реквизитам происходит только на стороне клиента.
Если вам известен уникальный идентификатор объекта, то вы можете получить ссылку на стороне клиента без обращений к серверу:
var текСсылка = клиент.Документы.ЗаказКлиента.ПолучитьСсылку(new Guid("5a32b6ab-4661-11e9-912a-38d547755ef7"));
Если вам нужна ссылка на предопределенный элемент, то получить ее предельно просто:
var ставкаНДС = клиент.Перечисления.СтавкиНДС.НДС18_118;
var рольИсполнителя = клиент.Справочники.РолиИсполнителей.ОтветственныйЗаКонтрольИсполнения;
Редактирование объектов
Читать данные объектов мы теперь умеем. Давайте попробуем их отредактировать:
var заказ = клиент.Документы.ЗаказКлиента.НайтиПоНомеру("ТД00-000018", new Date(2017, 1, 1));
var заказОбъект = заказ.ПолучитьОбъект();
заказОбъект.Дата = DateTime.Today;
заказОбъект.Номер = "ТД00-000055";
заказОбъект.Товары.Очистить();
var стр = заказОбъект.Товары.Добавить()
стр.Номенклатура = клиент.Справочники.Номенклатура.НайтиПоКоду("000000104");
стр.Количество = 3;
заказОбъект.Записать(РежимЗаписиДокумента.Проведение);
В данном примере мы получили объект документа из ссылки, изменили реквизиты и табличную часть. Теперь попробуем создать номенклатуру и поместить ее в группу, которую тоже предварительно создадим:
// Создаем контекст группы справочника
var группаОбъект = клиент.Справочники.Номенклатура.СоздатьГруппу();
группаОбъект.Наименование = "Новая группа";
группаОбъект.Записать();
// Создаем контекст элемента справочника
var товарОбъект = клиент.Справочники.Номенклатура.СоздатьЭлемент();
товарОбъект.Родитель = группаОбъект.Ссылка;
товарОбъект.Наименование = "Новый товар";
товарОбъект.Артикул = "T-00012321";
// Записываем объект справочника
товарОбъект.Записать();
// Получаем ссылку на созданный объект
var товарСсылка = товарОбъект.Ссылка;
Теперь редактировать объекты мы тоже умеем! Двигаемся дальше!
Формирование выборок
Зачастую в клиентском приложении требуется получить список элементов справочника, документов и прочих коллекций. Для этого в библиотеке предусмотрен класс "Селектор". Селектор позволяет формировать выборку ссылок из указанной коллекции с учетом сложных отборов и сортировок. Вместе со ссылками могут быть запрошены и контекстные данные объектов.
var группаМебель = клиент.Справочники.Номенклатура.НайтиПоНаименованию("Мебель");
var текСелектор = клиент.Справочники.Номенклатура.СоздатьСелектор();
текСелектор.
Выбрать("Наименование, Код, Производитель, Производитель.ИНН").
Где("ЭтоГруппа", false).
Где("Ссылка", группаМебель, ВидСравнения.ВИерархии).
Упорядочить("Производитель").
Упорядочить("Наименование", НаправлениеСортирвки.Убывание);
foreach (var текСсылка in текСелектор) {
Console.WriteLine("Наименование: {0}; Код: {1}, Производитель: {2}; ИНН: {3}",
текСсылка.Наименование,
текСсылка.Код,
текСсылка.Производитель,
текСсылка.Производитель.ИНН
);
}
// Сохраняем результат выборки в массив для последующего использования
var результат = текСелектор.ВыгрузитьРезультат();
Мы только что выбрали всю номенклатуру, которая находится иерархически в группе "Мебель", и упорядочили ее по двум полям. Вместе с выборкой загрузились данные перечисленных выше полей. Теперь эти данные сохранены на стороне клиента и обращение к ним не будет приводить к серверным вызовам.
Полагаю, принцип работы селектора понятен! Продолжим!
Выполнение запросов
Иногда возможностей селектора становится недостаточно, и нам нужно получить данные с помощью произвольного запроса. Давайте создадим и выполним простейший запрос:
var запрос = клиент.СоздатьЗапрос(@"
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка,
Номенклатура.Код КАК Код,
Номенклатура.Наименование КАК Наименование
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Артикул = &Артикул
");
запрос.УстановитьПараметр("Артикул", "Т-0001");
var результат = запрос.Выполнить();
foreach (var стр in результат) {
Console.WriteLine((стр.Код, стр.Наименование));
}
Мы только что выполнили простой запрос с одним параметром. Давайте теперь выполним шаблонизированный запрос:
var текЗапрос = клиент.СоздатьЗапрос(@"
ВЫБРАТЬ ПЕРВЫЕ 5
ЦеныНоменклатурыСрезПоследних.Номенклатура КАК Номенклатура,
ЦеныНоменклатурыСрезПоследних.Характеристика КАК Характеристика,
ЦеныНоменклатурыСрезПоследних.Цена КАК Цена
{ВЫБРАТЬ
Номенклатура.*}
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
{(&Период)},
{ (Номенклатура).*, (Характеристика).*}
) КАК ЦеныНоменклатурыСрезПоследних
{ГДЕ
ЦеныНоменклатурыСрезПоследних.Цена}
{УПОРЯДОЧИТЬ ПО
Номенклатура.*,
Характеристика.*}
");
// Добавляем дополнительное поле запроса с указанием синонима поля
текЗапрос.ДобавитьПоле("Номенклатура.Производитель.Наименование", "Бренд");
// Добавляем дополнительный отбор по цене
текЗапрос.ДобавитьУсловиеОтбора("Цена", 100, ВидСравнения.БольшеИлиРавно);
// Указываем дополнительное упорядочение по двум полям
текЗапрос.ДобавитьУпорядочение("Номенклатура.Производитель");
текЗапрос.ДобавитьУпорядочение("Характеристика", НаправлениеСортировки.Убывание);
var результат = текЗапрос.Выполнить(ОбходРезультатаЗапроса.Прямой);
Все запросы выполняются через построитель, поэтому на клиентской стороне доступны методы построителя. С помощью них мы добавили в запрос новое поле, указали дополнительные отборы и сортировки.
По умолчанию, результат запроса возвращается в виде таблицы значений, но можно указать тип обхода результатов, например "По группировкам", и мы получим дерево значений.
Если вам нужно выполнить пакетный запрос, то вместо функции "Выполнить" достаточно вызвать "ВыполнитьПакет". Результатом будет массив таблиц или массив деревьев, в зависимости от указанного типа обхода.
Обучающие материалы
Мы записали обучающие видеоролики, которые демонстрируют работу расширения:
- Установка и настройка расширения
- Установка и настройка клиентской библиотеки для .Net Core
- Вызов процедур и функций 1С из .Net Core
- Работа со ссылками и объектами
- Работа с выборками
- Выполнение запросов
- Работа с метаданными
Заключение
В данной статье возможности расширения продемонстрированы совсем кратко. Подробную информацию о расширении и клиентских библиотеках вы можете прочитать в официальной документации.
На текущий момент расширение и клиентские модули находятся в стадии beta-тестирования и распространяются по свободной лицензии.