Обмен данными с поставщиками нужно автоматизировать. 21-й век на дворе. Понятно, что в идеальном мире нужно созвать конференцию поставщиков и потребителей в данной отрасли (в моем случае это автозапчасти), на которой согласовать единый формат обмена информацией и всем будет счастье. У аптечников такое есть. Но мы живем в неидеальном мире.
Изначальная идея у всех одна: поставщик готов отдать вам элементы своих справочников и готов принять от вас заказ. Но у всех поставщиков бизнес-процессы имеют свои особенности и это накладывает на API поставщиков разные начальные требования, а разные программисты, их реализующие, доводят картину до того, что у API разных поставщиков нет общих черт, кроме изначальной идеи.
Как написать обмен в пятидесяти форматах? Написать пятьдесят обменов? Давайте предположим, что в лучшем случае обмен с одним поставщиком писать и тестировать 1 неделю и ставка программиста 3000р/час. Умножили? И это я еще про поддержку и администрирование всего этого зоопарка не вспоминал.
Есть другие варианты? Мой ответ: написать один обмен!
Мы должны придумать себе 51-го поставщика и написать обмен с ним. Это должен быть идеальный для нас, для наших процессов поставщик. Этот поставщик должен иметь общие черты всех поставщиков, отличаясь от них в деталях.
Мы должны начать мыслить не «от поставщика», а «от нас» и картина моментально упростится. У нас то бизнес-процесс один и программист тоже один и все, что выходит за рамки разумного по нашему мнению — нам не особо интересно. Понятно, что такой подход неизбежно приведет к «загрублениям» в логике и какая-то «тонкая» функциональность пойдет под нож, но такова цена простоты.
Таких загрублений я насчитал три вида:
- Функциональность, нужная нам, но которую некоторые поставщики будут неготовы обеспечить
- Функциональность которую поставщики предоставляют, но она нам не нужна.
- Функциональность реализованная кардинально разными методами у разных поставщиков.
Придумывая своего идеального поставщика, я перелопатил документацию поставщиков к их API и выработал 4 основных метода, при помощи которых я разработал API своего идеального поставщика.
Метод 1: Обобщение
Обобщение — это основной принцип разработки какой угодно функциональности. Обмен с поставщиками не исключение.
Например, один поставщик может использовать статусы заказов «принят», «в работе», «отгружен», «отказан». Другой «в работе менеджера», «в работе на складе», «в работе транспортной компании», «закрыт успешно», «отменен». Еще у одного поставщика я видел 27 разных статусов. Серьезно, я специально их посчитал. Понятно, что в нашей системе весь этот зоопарк нам не нужен. Мы обобщим статусы до минимально необходимого нам набора: «новый», «в работе поставщика», «отгружен поставщиком», «проблема».
Далее останется просто сопоставить каждый элемент справочника каждого поставщика с элементом нашего справочника.
Метод 2: Разделение
Обобщить можно не все. Иногда приходится разделять.
Например, если один поставщик принимает заказ целиком (все строки заказа в одном XML), а другой по принципу корзины интернет-магазина (добавить товар в корзину — добавить товар в корзину — заказать корзину) то написать такое каким-то «общим» образом не получится.
В этом случае обмен с нашим идеальным 51-м поставщиком таки должен разделиться. т.е. он должен опционально уметь отправлять заказ и одним и другим методом. У меня это называется «метод отправки заказа» и имеет он два варианта значения: «заказ целиком» и «построчно».
А еще есть настройка «метод проверки статуса» и он имеет уже три варианта: «запрос по заказу — возврат по заказу», «запрос по заказу — возврат построчно» и «запрос построчно — возврат построчно».
Но увлекаться разделением сильно не стоит, поскольку каждое дробление ведет к усложнению системы. И чего точно не стоит делать — это допускать древовидное разделение. Старайтесь все свести к плоским настройкам. Если дробление функциональности по одному признаку будет зависеть от дробления по другому — вы закопаетесь в настройках и вместо элегантной простоты получите ад настроек.
Метод 3: Отсечение
Метод отсечения интуитивно понятен и применяется в тех случаях, когда функциональность поставщика превосходит ту, которая необходима вам.
Например, поставщик может предоставлять возможность редактировать заказ до какого-то определенного момента (например, до передачи в набор).
Но нам это не особо интересно и все остальные поставщики такой возможности не имеют, соответственно нам нужно просто сказать, что мы не будем пользоваться такой возможностью.
Нужно четко обозначить границы автоматизации. Все что выходит за их рамки должно быть исключено из рассмотрения. Игнорируйте аргументы «это дополнительное удобство»: если с остальными поставщиками можно обойтись без этого — значит и с этим можно.
Метод 4: Умолчание
Метод умолчания является обратной крайностью метода отсечения. В данном случае, проблема заключается в том, что граница автоматизации включает в себя функциональность, которую не поддерживает поставщик. Например, возврат статуса заказа. Некоторые поставщики могут этого не поддерживать.
В этом случае не стоить городить во всех последующих алгоритмах проверку на то, считать ли статус заказа актуальным или игнорировать его, потому, что поставщик не умеет его возвращать. Гораздо проще поставить «заглушку», которая будет переводить такие заказы в последний успешный статус.
Таким образом, проанализировав ваши потребности и возможности — вы можете начертить границы автоматизации. Это и будет завершением этапа проектирования API вашего идеального 51-го поставщика. Т.е., если вдаваться в технику — поставщиков в системе все равно будет 50, но код выгрузки/загрузки данных будет один и тот же и будет формировать XML для всех 50 поставщиков в формате 51-го поставщика.
Так получилось, что в этой статье я заострил внимание на выгрузке заказа, как на наиболее вариативной и сложной части обмена (по крайней мере у меня), но все написанное можно применять и к загрузке и к выгрузке и заказа и мастер-данных и чего-угодно еще.
Далее необходимо обдумать, как лучше устроить файл выгрузки для идеального 51-го поставщика:
- Во-первых это должен быть XML (почему — станет ясно позднее).
- Во-вторых он должен содержать максимальное количество данных, которые вы только можете выгрузить с этим заказом. Т.е. на этом этапе нам не важно, требуется ли указывать ставку НДС для поставщика, которому в конечном итоге предназначен заказ — мы должны выгружать все что может потребоваться.
- В-третьих конвертируемые значения должны быть сконвертированы. Т.е. уже на стадии формирования XML для идеального 51-го поставщика, например, идентификаторы товаров должны быть подставлены от того поставщика, которому предназначен заказ. Это не должно вызвать каких-то затруднений, поскольку все данные для этого в заказе есть.
Таким образом, у нас должен получиться файл, содержащий все данные, нужные поставщику чтобы принять заказ, но в немного невалидном виде )
Так как же оно работает?
Пора переходить от теории к делу, от идеальных поставщиков к реальным. Думаю все кому интересно уже догадались что тот XML, который у нас получился, мы будем преобразовывать хитрым способом, чтобы из одного формата получить 50. Для этого мы будем использовать двухступенчатое преобразование:
- XSLT
- Форматный конвертер
Сначала нам нужно создать саму структуру данных. Для этого лучше всего подходит XML, поскольку в нашем распоряжении имеется мощный язык преобразования XML-документов (XSLT — eXtensible Stylesheet Language Transformations), позволяющий преобразовать XML документ из одного вида в другой.
Язык не сложный, освоить на базовом уровне его можно за пару-тройку дней, а 99% моих XSLT обходятся всего двумя инструкциями value-of и for-each.
Далее, созданную структуру, возможно, потребуется преобразовать в формат, отличный от XML. В этом смысле наиболее сложным представляется преобразование XML <-> JSON, пример такого преобразования я выкладывал. (но вам не покажу, потому что НЛО запретило вставить сюда ссылку на Инфостарт) Бывает еще преобразование XML в параметры GET запроса, т.е. в строку вида ?item=12345&qty=4&price=19.50, но это уже совсем просто. В результате этого шага должен появиться текст обмена, валидный для конкретного поставщика.
После получения валидного текста для обмена — его остается только отправить. Этой задачей занимается функциональность, которую я условно называю универсальный дозвонщик. Она умеет по настройкам вызвать SOAP или REST сервис и передавать туда результат работы конвертера. Дозвонщик должен предусматривать очень разные сценарии развития событий: он должен быть готов отправить данные как параметры и как тело запроса, POST и GET методами, с basic и не очень авторизацией и т.п.
Теоретически, к Дозвонщику можно прикрутить хоть чтение/запись в эксель, конкретные «коннекторы» выбирайте под свои потребности.
В принципе, функции получения данных в формате идеального 51-го поставщика, XSL трансформации, форматного конвертера и дозвонщика можно вынести в отдельный инстанс, не связывая эту функциональность с какой-либо из существующих систем, что у меня и сделано. Я называю эту штуку Конвертер интерфейсов.
Код Конвертера интерфейсов умещается в 300 строк. Основные настройки (тексты XSLT, настройки преобразований и вызовов) хранятся как данные. В итоге процесс отправки заказа поставщику выглядит следующим образом:
- ERP формирует заказ и отправляет его в формате 51-го поставщика Конвертору интерфейсов
- Конвертер получает тест XML в формате 51-го поставщика и дообогащает его
- определяет какому поставщику нужно отправить запрос (условно, по коду интерфейса), — на лету выполняет XSL трансформацию и форматную конвертацию
- тут же отправляет преобразованный запрос дальше поставщику
- получает ответ поставщика
- из тела ответа в обратном порядке выполняет форматную конвертацию, дообогащение, далее XSL трансформацию в формат ответа идеального поставщика
- возвращает в ERP ответ идеального поставщика
Все это происходит «на лету», т.е. синхронно, т.е. ERP думает что обменивается со всеми поставщиками в одном, удобном ей формате, даже не замечая что происходит что-то неладное. Такой подход, естественно, применим только для случаев передачи относительно небольших пакетов данных. Если у вас ходят прайс-листы по 1 Гб — стоит подумать как ко всей этой истории прикрутить асинхрон.
Фишка с дообогащением
Почти в каждом обмене есть такие значения, которые просто хардкодятся. Частично это может быть следствием через чур богатой функциональности поставщика, например, параметр «процент на который может быть увеличена цена товара без дополнительного согласования», частично это просто какие-то значения типа «для вас это всегда 4000». Что 4000? Почему 4000? Просто 4000 и все. Чтобы не хранить в ERP такие «мертвые» значения, в Конвертере есть возможность дообогатить данные ERP каким-то еще XML'ем. Т.е. это просто произвольный XML, который хранится в БД Конвертера интерфейсов.
Для каждого интерфейса (поставщика) такой XML свой. Дообогащение происходит по принципу простой склейки с обёрткой в общий тег, например:
<body>
  <ERPData>
    ...<i>данные из ERP</i>...
  </ERPData>
  <AdditionalData>
    ...<i>статичные данные Конвертера</i>...
  </AdditionalData>
</body>
И далее, такой обогащенный XML поступает на вход XSLT движка, где доступны как данные, отправленный ERP, так и наши статичные данные.
Что мы имеет в результате?
Мы ведь не за просто так весь этот огород городить начали, а потому что хотели сократить время на подключение поставщика. Вот что из себя представляет подключение нового поставщика:
- Изучение документации к API
- Создание соответствующего поставщика в ERP и его настройка (порядка 10 галочек).
- Описание преобразования текста обмена на языке XSLT
- Описание форматной конвертации (если требуется)
- Настройка параметров исходящего подключения (еще 10 галочек).
На практике, все эти действия занимают до 3-х часов. Понятно, что бывают случаи когда поставщик выдумает что-то «эдакое», но в 95% случаев можно уложиться в 2-3 часа. (в этом предложении я ошибся и написал «подставщик» вместо «поставщик». Совпадение?..)
Естественно, есть еще масса деталей и нюансов, рассказать о которых в рамках обзорной статьи не представляется возможным, однако цель статьи: показать один из возможных подходов к проектированию архитектуры мультиформатного обмена, я считаю достигнутой.
Комментарии (11)
antonn
13.04.2018 15:57Почему бы не рассмотреть при работе с поставщиками существующие EDI (desadv/recadv/orders/pricat и тп)? Есть и провайдеры для этого, кому сложно интегрироваться самостоятельно.
m-rv Автор
13.04.2018 16:15С одной стороны это не бесплатно, с другой поставщиков «прогибать» под единый формат все равно нужно. Большие EDI имеют смысл, когда поставщиков и производителей много с обеих сторон.
apapacy
13.04.2018 18:45Интересно какая у Вас номенклатура приобретаемых товаров?
Тут бывают изредка обсуждения сложности такого обмена с разными системами данными по номенклатуре (прайсы, заказы и т.п.). И собственно основная проблема это идентификация товаров. Как сопоставить что две единицы которые имеют своершенно разое наименование это одно и то же. А очень похожие с точки зрения всяких там индексов сравнения намиенования, которые из 65 символов отличаются ровно одним символом — это совершенно разные номенклатурные позиции. С этим как Вы разбираетесь?m-rv Автор
14.04.2018 08:40У нас жесткие критерии, никаких индексов сравнения. Гораздо интереснее, что иногда даже человек не может сказать две запчасти, лежащие перед ним — это одна позиция или нет. Напр. какой-то электронный датчик на корпусе которого написано Bocsh 12345 может быть двумя позициями, в зависимости от того, упакован он в пленочку Volkswagen или Skoda (а пленочка, понятное дело может быть потеряна).
newohoenix
14.04.2018 12:37По поводу аптек, часто с ними работаем, у них тоже зоопарк, например, есть фармхаб и фарм см, плюс ряд сетей аптек написало свои обмены на основе упомянутых систем, которые представляют из себя dbf файлы с самой разнообразной структурой.
vtulin
14.04.2018 15:21+1Я как увидел XSLT затаил дыхание, это и правда очень мощная и древняя магия:)
Предстоит мне скоро решать похожие проблемы, боюсь там будет ужас-ужас и эксель файлы.
Bsplesk
16.04.2018 07:17Сначала нам нужно создать саму структуру данных. Для этого лучше всего подходит XML,
— всю жизнь «структура данных» описывается в стеке «xml/xsd/xlst» — xsd(XML Schema Definition) — схемой, xsd — также является валидным xml, но вот самим xml «структуру» данных описать не получится — это формат передачи.
Довольно «стандартная наколеночная реализация», если чуть усложнить и допустить, что данные требуются не только ERP, то для таких вещей целесообразно использовать шины/брокеры/очереди (sonic/ESB/IIB… etc), которые поддерживают всякие необходимые сопутствующие «плюшки».
Самые интересные вопросы:
— ERP — ваша вероятно будет развиваться и 51 формат эволюционирует в 52-53-54 — модель данных может измениться (динамическая) — как быстро Вы/новый человек команды можете изменить модель данных? — если она «прибита гвоздями» это не хорошо.
— Поставщики вероятно, аналогично могут эволюционировать (изменить модель данных) — как быстро быстро новый человек может разобраться и приступить к работе? (xlst/xsl — это хорошо — это стандарт, а что из себя представляет «форматные конвертер»? — «spaghetti code»? или применяете что-то интересное?)
— Что будет если новый поставщик не вписывается в текущую 5X модель?
— и самое интересное — как при такой архитектуре разбирает конфликты с поставщиком?
m-rv Автор
16.04.2018 07:29про структуру: в строгом понимании вы конечно правы, но в рамках повествования это был самый удачный термин, который я смог найти.
про эволюцию в 52-й формат: я бы не сказал что есть риск перехода от 51-го к 52-му формату. есть перспектива развития 51-го формата — это естественный процесс, нужно только следить, чтобы эволюция проходила с учетом обратной совместимости.
про эволюцию поставщиков: если поставщик захочет изменить формат — это, в общем случае, ничем не будет отличаться от подключения нового поставщика.
про форматный конвертер: наберите в гугле «xml — json преобразователь» — первая ссылка будет на то, как это делается.
про конфликты: не совсем понятно что имеется ввиду, если отказ поставщика принять заказ — то ответ поставщика также принимается, конвертируется и отдается в ERP.
oleshko
РОман, а обмены у вас на 1С реализованы?
m-rv Автор
Да, это все на 1Се написано, но суть не в платформе