Обмен с внешними системами целиком в расширении: что положить в cfe, чтобы типовая осталась на поддержке
Типичный разговор с заказчиком начинается не с вопроса «как сделать обмен», а с вопроса «а нас не снимут с поддержки?». Страх понятный: многие видели базы, где ради загрузки заказов с сайта «чуть-чуть поправили» модуль документа, и через полгода обновление превратилось в проект на неделю.
Расширения конфигурации давно позволяют обойтись без этого. Но «сделать в расширении» — не магическое заклинание: можно написать расширение, которое ломается на каждом релизе так же, как правленная типовая. Ниже — как я раскладываю интеграцию с внешними источниками (сайт, CRM, своя программа заказчика) по объектам расширения, чтобы у неё была одна точка входа, одна точка выхода и минимальная поверхность соприкосновения с типовой. Примеры кода упрощены и написаны для статьи.
Принцип: своё — своим, типовое — только через публичное поведение
Правило, из которого выводится всё остальное:
Всё, что относится к обмену, — собственные объекты расширения. С типовыми объектами расширение взаимодействует так же, как пользователь или типовая обработка: создаёт и записывает документы, читает справочники. Заимствуем минимум и только там, где без этого никак.
Чем меньше заимствованных объектов и перехваченных методов, тем меньше мест, которые может задеть следующий релиз.
Что лежит в расширении
| Объект расширения | Назначение |
|---|---|
HTTP-сервис ВходящиеДанные |
Единая точка входа: /in/{source}. Принимает, проверяет, сохраняет |
Справочник ИсточникиДанных |
Источник: код, способ проверки подписи, ответственный, активность |
Регистр сведений ВходящиеСообщения |
Сырое тело, источник, внешний ID, статус, текст ошибки, число попыток |
Регистр сведений СоответствиеОбъектов |
Источник + внешний ключ → ссылка на объект 1С |
Регистр сведений ОчередьИсходящих |
Единая точка выхода: что и кому отправить |
Общие модули Обмен_Прием, Обмен_Разбор, Обмен_Отправка |
Логика, разнесённая по ответственности |
| Регламентные задания | Разбор входящих, отправка исходящих, страховочный опрос источников |
Роль Обмен_Администратор |
Права на настройки и ручную перезагрузку |
Обработка Обмен_Монитор |
Очередь «Требует внимания», ошибки, тишина |
Обратите внимание, чего здесь нет: реквизитов, добавленных в типовые справочники и документы, переопределённых форм типовых объектов, методов с аннотацией &Вместо.
Почему внешние ID — в своём регистре, а не в реквизите типового справочника
Расширение умеет добавлять реквизиты заимствованным объектам, и это соблазнительно: «добавим в Номенклатуру реквизит ВнешнийID, и всё». Минусы:
- расширение начинает менять структуру таблицы типового объекта — на большой базе обновление расширения может потребовать реструктуризации;
- у одного товара бывает несколько внешних ID: на сайте, в CRM, в программе склада. Один реквизит превращается в три, потом в табличную часть;
- при отключении расширения данные в этих реквизитах становятся недоступны вместе с ним, а регистр соответствий хотя бы явно принадлежит обмену.
Свой регистр СоответствиеОбъектов с измерениями «Источник», «ТипОбъекта», «ВнешнийКлюч» и ресурсом «Ссылка» закрывает все три пункта и не трогает типовые таблицы.
Функция НайтиПоВнешнемуКлючу(Источник, ТипОбъекта, ВнешнийКлюч) Экспорт
Запрос = Новый Запрос(
"ВЫБРАТЬ ПЕРВЫЕ 1
| С.Ссылка КАК Ссылка
|ИЗ
| РегистрСведений.Обмен_СоответствиеОбъектов КАК С
|ГДЕ
| С.Источник = &Источник
| И С.ТипОбъекта = &ТипОбъекта
| И С.ВнешнийКлюч = &ВнешнийКлюч");
Запрос.УстановитьПараметр("Источник", Источник);
Запрос.УстановитьПараметр("ТипОбъекта", ТипОбъекта);
Запрос.УстановитьПараметр("ВнешнийКлюч", НРег(СокрЛП(ВнешнийКлюч)));
Выборка = Запрос.Выполнить().Выбрать();
Возврат ?(Выборка.Следующий(), Выборка.Ссылка, Неопределено);
КонецФункции
Точка входа: один HTTP-сервис на все источники
Один сервис с шаблоном /in/{source}, а не отдельный сервис на сайт, CRM и программу склада. Новый источник — новая запись в справочнике и новая функция разбора формата, а не новый объект метаданных и новое обновление расширения.
Сервис делает три вещи: проверяет, что источник известен и запрос подлинный; сохраняет тело в ВходящиеСообщения; отвечает. Разбор — отдельно, регламентом или сразу после записи, если источнику нужен синхронный ответ.
Функция InPOST(Запрос)
Источник = Обмен_Прием.ИсточникПоКоду(Запрос.ПараметрыURL["source"]);
Если Источник = Неопределено Тогда
Возврат Обмен_Прием.Ответ(404);
КонецЕсли;
Если Не Обмен_Прием.ЗапросПодлинный(Источник, Запрос) Тогда
Возврат Обмен_Прием.Ответ(401);
КонецЕсли;
Ид = Обмен_Прием.Сохранить(Источник,
Запрос.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8));
Возврат Обмен_Прием.Ответ(200, Ид);
КонецФункции
Публикация: HTTP-сервисы расширения публикуются на веб-сервере так же, как сервисы основной конфигурации. Если источник в интернете, а база в офисе — нужен HTTPS снаружи или буфер между ними; это вопрос инфраструктуры, о нём лучше договориться с админом заказчика в первый день.
Создание типовых документов без вмешательства в типовой код
Входящие данные в итоге должны стать типовыми документами: заказ клиента, реализация, поступление в кассу и т. д. Расширение создаёт их программно — так же, как это сделала бы типовая обработка.
Функция СоздатьЗаказКлиента(Данные) Экспорт
Документ = Документы.ЗаказКлиента.СоздатьДокумент();
// Типовое заполнение по умолчанию: организация, склад, валюта и т. п.
Документ.Заполнить(Новый Структура("Контрагент", Данные.Контрагент));
Документ.Дата = Данные.Дата;
Документ.Комментарий = СтрШаблон("Источник: %1, № %2", Данные.Источник, Данные.ВнешнийНомер);
Для Каждого СтрокаДанных Из Данные.Товары Цикл
Строка = Документ.Товары.Добавить();
Строка.Номенклатура = СтрокаДанных.Номенклатура;
Строка.Количество = СтрокаДанных.Количество;
Строка.Цена = СтрокаДанных.Цена;
// Пересчёт сумм — типовыми средствами конфигурации, если они есть
КонецЦикла;
Если Не Документ.ПроверитьЗаполнение() Тогда
ВызватьИсключение Обмен_Разбор.ТекстСообщенийПользователю();
КонецЕсли;
Документ.Записать(РежимЗаписиДокумента.Запись);
Возврат Документ.Ссылка;
КонецФункции
Несколько правил, которые экономят время:
- Вызывайте
Заполнить(), а не заполняйте всё руками. Типовая сама подставит значения по умолчанию из настроек пользователя и организации — и продолжит это делать после обновлений. ПроверитьЗаполнение()перед записью — дешёвый способ получить понятную ошибку («не заполнен склад»), а не исключение из глубин проведения.- Пересчёт сумм, НДС, скидок — вызывайте типовые процедуры, если они экспортные и стабильны. Если нет — считайте сами, но в одном месте, и покрывайте проверкой на копии после каждого обновления.
- Проводить или нет — решение заказчика. Часто правильнее записать без проведения и оставить проведение человеку на первые недели.
Имена реквизитов в примере условные: в УТ, КА, ERP, УНФ и БП они различаются, и это главная причина, по которой «универсальная обработка из интернета» обычно требует доработки под конкретную конфигурацию.
Точка выхода: своя очередь вместо правки типового плана обмена
Когда 1С должна сама отправлять данные наружу (остатки и цены на сайт, статусы заказов в CRM), появляется желание включить типовые объекты в состав типового плана обмена. Это правка типовой.
Альтернатива — своя очередь. Расширение подписывается на запись нужных объектов и кладёт в регистр ОчередьИсходящих запись «получатель + объект + время». Отдельное регламентное задание выбирает очередь порциями, формирует пакеты и отправляет.
// Подписка на событие в расширении: источник — нужные типовые документы
Процедура Обмен_ЗарегистрироватьИзменение(Источник, Отказ) Экспорт
Если Отказ Или Источник.ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
Если Обмен_Отправка.ИдетЗаписьИзОбмена() Тогда
Возврат; // не отправляем обратно то, что только что пришло
КонецЕсли;
Обмен_Отправка.ПоставитьВОчередь(Источник.Ссылка);
КонецПроцедуры
Проверка «запись идёт из обмена» нужна, чтобы документ, созданный по заказу с сайта, не уехал обратно на сайт как «изменённый в 1С» и не запустил пинг-понг.
Как пережить обновление типовой
Расширение не делает обновления бесплатными. Оно делает их предсказуемыми. Что для этого нужно:
- Минимум заимствований. Каждый заимствованный объект — контролируемые свойства, которые платформа сверит при обновлении. Если документ создаётся через
Документы.X.СоздатьДокумент()из своего общего модуля, сам документ заимствовать не нужно. - Никаких
&Вместона типовых методах. Если без перехвата не обойтись —&Послеили&ИзменениеИКонтроль: второй честно перестанет применяться, если типовой фрагмент изменился, и вы узнаете об этом при обновлении, а не из отчётов через месяц. - Проверка после каждого обновления на копии. Сценарий короткий: прогнать сохранённые входящие сообщения через разбор, убедиться, что документы создаются и проводятся, а очередь исходящих отправляется. Этот прогон стоит договорить с заказчиком заранее как часть сопровождения.
- Безопасный режим и защита от опасных действий — осознанно. Расширению, которое ходит по HTTP наружу, могут понадобиться разрешения; это решение администратора базы, а не галочка «чтобы заработало».
Мониторинг — в той же обработке, что и разбор
Одна обработка Обмен_Монитор с тремя закладками закрывает почти все вопросы поддержки:
- Требует внимания — сообщения, которые не удалось сопоставить: незнакомый товар, контрагент без ИНН. Кнопка «Сопоставить» запоминает связь в
СоответствиеОбъектов, кнопка «Разобрать заново» перезапускает сообщение из сохранённого тела; - Ошибки — исключения с подробным текстом;
- Тишина — источники, от которых давно ничего не приходило при обычном потоке.
Оповещение ответственному — по каждой закладке, сгруппированно, а не письмом на каждое сообщение.
Что получаем в итоге
- Типовая конфигурация остаётся на поддержке и обновляется как раньше.
- Всё, что относится к обмену, — в одном cfe: его можно отключить, обновить, передать другому специалисту.
- Новый источник данных — это запись в справочнике, функция разбора формата и правила сопоставления, а не новая архитектура.
- Настройки и ручные операции (сопоставление, перезагрузка) — в режиме предприятия, без конфигуратора и без выгона пользователей из базы.
Расширение работает не везде: базовые версии расширения не принимают, у облачных сервисов вроде 1С:Фреш свои правила для расширений. Это стоит проверить до того, как обещать заказчику сроки.
Если вы раскладываете обмены по-другому — например, добавляете реквизиты в типовые объекты и вас это устраивает — напишите в комментариях, как переживаете обновления. Интересно сравнить подходы.
Вступайте в нашу телеграмм-группу Инфостарт
Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».
- 0% комиссии — оплата напрямую исполнителю;
- Исполнители любого масштаба — от отдельных специалистов до команд под проект;
- Прямой обмен контактами между заказчиком и исполнителем;
- Безопасная сделка — при необходимости;
- Рейтинги, кейсы и прозрачная система откликов.