Pull to refresh

Как написать обмен с 50 поставщиками и не сойти с ума

Reading time8 min
Views9.6K
Поставщики бывают разные. Одни готовы подстроиться под наш формат — другие нет; одни обмениваются SOAP'ом — другие REST'ом; одни работают с кодами товаров — другие с идентификаторами предложений; одни готовы отдать статус по заказу — другие нет; у одних есть справочники, элементы которых вам нужно сопоставить со своими — у других нет. В общем очень разные.


Обмен данными с поставщиками нужно автоматизировать. 21-й век на дворе. Понятно, что в идеальном мире нужно созвать конференцию поставщиков и потребителей в данной отрасли (в моем случае это автозапчасти), на которой согласовать единый формат обмена информацией и всем будет счастье. У аптечников такое есть. Но мы живем в неидеальном мире.

Изначальная идея у всех одна: поставщик готов отдать вам элементы своих справочников и готов принять от вас заказ. Но у всех поставщиков бизнес-процессы имеют свои особенности и это накладывает на API поставщиков разные начальные требования, а разные программисты, их реализующие, доводят картину до того, что у API разных поставщиков нет общих черт, кроме изначальной идеи.

Как написать обмен в пятидесяти форматах? Написать пятьдесят обменов? Давайте предположим, что в лучшем случае обмен с одним поставщиком писать и тестировать 1 неделю и ставка программиста 3000р/час. Умножили? И это я еще про поддержку и администрирование всего этого зоопарка не вспоминал.

Есть другие варианты? Мой ответ: написать один обмен!

Мы должны придумать себе 51-го поставщика и написать обмен с ним. Это должен быть идеальный для нас, для наших процессов поставщик. Этот поставщик должен иметь общие черты всех поставщиков, отличаясь от них в деталях.

image

Мы должны начать мыслить не «от поставщика», а «от нас» и картина моментально упростится. У нас то бизнес-процесс один и программист тоже один и все, что выходит за рамки разумного по нашему мнению — нам не особо интересно. Понятно, что такой подход неизбежно приведет к «загрублениям» в логике и какая-то «тонкая» функциональность пойдет под нож, но такова цена простоты.

Таких загрублений я насчитал три вида:

  • Функциональность, нужная нам, но которую некоторые поставщики будут неготовы обеспечить
  • Функциональность которую поставщики предоставляют, но она нам не нужна.
  • Функциональность реализованная кардинально разными методами у разных поставщиков.

Придумывая своего идеального поставщика, я перелопатил документацию поставщиков к их 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 свой. Дообогащение происходит по принципу простой склейки с обёрткой в общий тег, например:

&ltbody&gt
&nbsp&nbsp&ltERPData&gt
&nbsp&nbsp&nbsp&nbsp...<i>данные из ERP</i>...
&nbsp&nbsp&lt/ERPData&gt
&nbsp&nbsp&ltAdditionalData&gt
&nbsp&nbsp&nbsp&nbsp...<i>статичные данные Конвертера</i>...
&nbsp&nbsp&lt/AdditionalData&gt
&lt/body&gt

И далее, такой обогащенный XML поступает на вход XSLT движка, где доступны как данные, отправленный ERP, так и наши статичные данные.

Что мы имеет в результате?


Мы ведь не за просто так весь этот огород городить начали, а потому что хотели сократить время на подключение поставщика. Вот что из себя представляет подключение нового поставщика:

  • Изучение документации к API
  • Создание соответствующего поставщика в ERP и его настройка (порядка 10 галочек).
  • Описание преобразования текста обмена на языке XSLT
  • Описание форматной конвертации (если требуется)
  • Настройка параметров исходящего подключения (еще 10 галочек).

На практике, все эти действия занимают до 3-х часов. Понятно, что бывают случаи когда поставщик выдумает что-то «эдакое», но в 95% случаев можно уложиться в 2-3 часа. (в этом предложении я ошибся и написал «подставщик» вместо «поставщик». Совпадение?..)

Естественно, есть еще масса деталей и нюансов, рассказать о которых в рамках обзорной статьи не представляется возможным, однако цель статьи: показать один из возможных подходов к проектированию архитектуры мультиформатного обмена, я считаю достигнутой.
Tags:
Hubs:
Total votes 7: ↑7 and ↓0+7
Comments12

Articles