Русский
Русский
English
Статистика
Реклама

Интеграция

Интеграция в системах контроля доступа

07.08.2020 12:20:56 | Автор: admin
Одна из основных тенденций рынка систем контроля доступа упрощение интеграции с другими системами: системами видеонаблюдения, охранно-пожарной сигнализации, управления предприятием, билетными системами.



Принципы интеграции


Одним из способов интеграции является передача SDK программного обеспечения стороннему ПО для управления контроллерами СКУД. При использовании Web-технологий упростить процесс интеграции позволяет реализация функций SDK в формате JSON API. Для интеграции также может применяться передача SDK контроллера стороннему ПО для управления контроллером. Еще один способ интеграции в СКУД использование дополнительных входов/выходов контроллера для подключения дополнительного оборудования: видеокамер, датчиков, устройств сигнализации, внешних верифицирующих устройств.

Построение комплексной системы безопасности




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

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

На территории объекта монтируются таблички о ведущемся видеонаблюдении. Для обнаружения и оценки используются видеокамеры и датчики охранной сигнализации.
Наличие у контроллеров дополнительных входов/выходов для подключения видеокамер, датчиков и устройств сигнализации обеспечивает аппаратное взаимодействие всех устройств комплексной системы безопасности. Например, при срабатывании пожарной сигнализации двери автоматически разблокируются. Камеры с функцией распознавания лиц способны передавать напрямую в контроллер информацию о личности проходящего, и контроллер принимает решение о разрешении или запрете доступа.

Интеграция СКУД с системами видеонаблюдения и охранной-пожарной сигнализации обеспечивает слаженную работу комплексной системы безопасности и позволяет осуществлять мониторинг ситуации и управление всеми устройствами системы в интерфейсе программного обеспечения СКУД. Для реализации обнаружения, оценки и реагирования сотрудники службы безопасности могут оперативно получать информацию о тревожных событиях и дистанционно оценивать ситуацию на экране монитора.

Например, при срабатывании пожарного извещателя данные от ближайшей видеокамеры автоматически выводятся на монитор. Сотрудник может оценить, действительно ли имеет место возгорание, или это ложная тревога. Это позволит быстро принять меры, не тратя времени на проверку события на месте.

Для расширения функционала СКУД может быть интегрирована с внешними верифицирующими устройствами: пирометрами, алкотестерами, весами, дозаторами антисептика. Предотвратить доступ нетрезвых сотрудников позволяет алкотестирование. Система контроля доступа может в онлайн режиме оповещать службы безопасности о положительных результатах на алкоголь, что позволяет оперативно реагировать на инциденты и вовремя проводить освидетельствование. Впоследствии в системе контроля доступа оператор имеет возможность формировать отчёты по результатам алкотестирования для получения информации о нарушителях режима и их количестве среди работников. Для предотвращения хищений можно организовать доступ с подтверждением от весов как внешнего верифицирующего устройства.

В условиях борьбы с коронавирусной инфекции все большее распространение получают системы контроля доступа, позволяющие осуществить интеграцию с пирометрами устройствами, измеряющими температуру тела, и бесконтактными дозаторами антисептика. В таких системах доступ на объект разрешается только при нормальной температуре тела и только после использования дезинфицирующей жидкости. Для реализации бесконтактной идентификации турникеты СКУД интегрируются с терминалами распознавания лиц и сканерами штрих-кодов.

Для упрощения процесса монтажа внешних верифицирующих устройств применяются специальные стойки и кронштейны: например, кронштейн для установки сканера штрих-кодов, стойка для алкотестера или терминала распознавания лиц.

Интеграция с системами документооборота и кадрового учета




Для автоматизации учета рабочего времени и контроля трудовой дисциплины СКУД может быть интегрирована ERP-системами, в частности с 1С. Учет рабочего времени производится на основании событий входа-выхода, регистрируемых контроллерами системы и передаваемых из системы контроля доступа в 1С. При интеграции производится синхронизация списков подразделений, организаций, должностей, ФИО сотрудников, графиков работы, событий и классификаторов.

Учет рабочего времени сотрудников может вестись как с помощью оборудования контроля доступа турникетов или замков со считывателями, так и с помощью специальных терминалов учета рабочего времени: стационарных или мобильных. Стационарные терминалы применяются на объектах, где нет необходимости устанавливать турникеты, или в случае удаления рабочих мест от проходной. Мобильные терминалы регистрации, для организации которых используется смартфон с NFC-модулем, применяются на удаленных объектах, где невозможно или нецелесообразно устанавливать стационарные терминалы.

Территория предприятия делится на рабочие зоны (кабинеты, цеха,) и нерабочие (кафе, комната для курения). На основе данных о входах и выходах сотрудников в рабочие и нерабочие зоны в системе строится табель учета рабочего времени, который передается в 1С для корректного начисления заработной платы.

Интеграция с билетными системами




Интеграция СКУД с билетными системами широко применяется на транспортных и спортивно-развлекательных объектах. Получение SDK контроллера доступа упрощает интеграцию с билетными системами и позволяет использовать контроллер в системах платного доступа: в фитнес-центрах, музеях, театрах, парках развлечений и многих других объектах.

На объектах массового пребывания людей билетная система может работать совместно с системой контроля доступа на базе распознавания лиц. При приобретении билета фото покупателя передается в базу данных системы и в дальнейшем выступает в качестве идентификатора. При покупке билетов онлайн идентификацию можно пройти, сделав селфи. Такие решения помогают минимизировать контакты сотрудников и посетителей объекта и предотвратить продажу фальсифицированных билетов.

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

Терминалы распознавания лиц в системах контроля доступа

11.09.2020 14:12:59 | Автор: admin
Распознавание лиц в системах контроля доступа отвечает растущему спросу на бесконтактные решения в области идентификации. Сегодня данный способ биометрической идентификации является общемировым трендом: среднегодовой рост объема рынка систем на базе распознавания лиц оценивается аналитиками в 20%. Согласно прогнозам, в 2023 году эта цифра увеличится до 4 млрд USD.



Интеграция терминалов с системой контроля доступа


Распознавание лиц в качестве способа идентификации может применяться в СКУД для контроля доступа, учета рабочего времени и интеграции с CRM- и ERP-системами. Лидирующими производителями терминалов распознавания лиц на российском рынке являются Hikvision, Suprema, Dahua и ZKteco.

Интеграция терминалов распознавания лиц с системой контроля доступа может осуществляться тремя способами, различие которых заключается в интерфейсе связи и функционале SDK. Первый способ позволяет добавлять новые данные сотрудников или посетителей непосредственно в интерфейсе СКУД, без добавления в терминалы за счет SDK терминала. При втором способе добавление новых пользователей осуществляется как в интерфейсе СКУД, так и непосредственно в терминалах, что менее удобно и более трудозатратно. В обоих случаях подключение осуществляется по интерфейсу Ethernet. Третий способ подключение по интерфейсу Wiegand, но в этом случае у терминалов и СКУД будут раздельные базы данных.

В обзоре будут рассмотрены решения с подключением по интерфейсу Ethernet. Возможность добавления пользователей в интерфейсе системы определяется SDK терминала.Чем шире возможности СКУД, тем больший функционал будет возможно реализовать при помощи терминалов. Например, интеграция системы контроля доступа PERCo-Web с терминалами Suprema позволяет добавлять данные непосредственно в интерфейсе ПО системы. Среди других возможностей занесение и сохранение фотографий сотрудников и посетителей для идентификации, осуществление конфигурации устройств и управления ими в режиме онлайн.

Все события проходов через терминалы сохраняются в системе. Система позволяет назначать алгоритм реакций на события, полученных с терминалов. При проходе сотрудника с распознаванием по лицу можно сформировать уведомляющее событие, которое будет отправляться на Viber или e-mail оператора системы. Система поддерживает работу с терминалами Face Station 2 и FaceLite от Suprema, ProfaceX, FaceDepot 7А, Facedepot 7 В, SpeedFace V5L от ZKteco. При проходе сотрудника или посетителя с повышенной температурой в СКУД формируется событие, на основании которого доступ может быть автоматически заблокирован.

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

Рассмотрим с точки зрения работы в СКУД технические характеристики следующих моделей данных производителей:

Face Station 2 и FaceLite от Suprema



ProfaceX, FaceDepot 7А, Facedepot 7 В, SpeedFace V5L от ZKteco



DS-K1T606MF, DS-K1T8105E и DS-K1T331W от Hikvision



ASI7223X-A, ASI7214X от Dahua



Защита от эмуляции


Распознавание лиц может базироваться на 2D или 3D- технологиях. Первая из них является более бюджетной, что сказывается и на стоимости терминалов. Среди ее недостатков высокие требования к освещению, более низкая в сравнении с 3D статистическая достоверность, неспособность учитывать мимику лица. Увеличить точность идентификации терминалов на базе 2D позволяют инфракрасные камеры.

Терминалы, работающие по 3D- технологии, являются более дорогостоящими, но обеспечивают высокую точность и достоверность идентификации и демонстрируют способность работы в условиях низкой освещенности. В терминалах Suprema и ZKteco для защиты от предъявления фотографии применяется детектирование живого лица, основанное на инфракрасной подсветке. Терминалы Hikvision используют алгоритм глубокого машинного обучения и детекции подлинности биометрических данных лица. Терминалы распознавания лиц от Dahua используют технологии искусственного интеллекта и глубинного изучения с поддержкой детекции витальности.

Скорость идентификации


Скорость идентификации терминалов распознавания лиц имеет особенно большое значение для объектов с интенсивным потоком посетителей: офисов крупных компаний, промышленных предприятий, мест массового пребывания людей. Высокая скорость идентификации препятствует образованию очередей и обеспечивает максимальную пропускную способность. Терминалы Hikvision DS-K1T331W, Dahua ASI7223X-A и ASI7214X распознают лица всего за 0,2 секунды. У модели DS-K1T606MF идентификация осуществляется за 0,5 секунд, у DS-K1T8105E менее чем за 1 секунду. Скорость идентификации терминалов Face Station и FaceDepot 7А составляет менее 1 секунды.

Двухфакторная идентификация




Удобным решением для работы в СКУД являются терминалы распознавания лиц, поддерживающие также и другие способы идентификации: например, доступ по карте, отпечатку пальца, ладони или смартфону. Такие решения позволяют усилить контроль доступа на объект за счет двухфакторной идентификации. Терминалы FaceLite и FaceStation 2 отличает наличие встроенного считывателя бесконтактных карт доступа, в других рассматриваемых нами моделях считыватель можно подключить дополнительно. Терминалы ZKteco поддерживают также идентификацию по ладони и коду. Терминалы Hikvision DS-K1T606MF поддерживают идентификацию по отпечаткам пальцев и картам Mifare, DS-K1T8105E имеет встроенный считыватель карт EM-Marine, к терминалу DS-K1T331W может быть подключен считыватель бесконтактных карт. Терминал ASI7214X также поддерживает работу с бесконтактными картами и отпечатками пальцев.

Измерение температуры


Одним из драйверов роста рынка решений по распознаванию лиц в системах контроля доступа стала пандемия Covid19, поэтому терминалы распознавания лиц с возможностью контроля температуры тела получили широкое распространение. Данный функционал из рассматриваемых нами моделей позволяют реализовать терминалы SpeedFace V5L, которые также определяют наличие на лице маски. Измерение температуры происходит бесконтактно, что снижает риск передачи инфекций и позволяет минимизировать
необходимость антисептической обработки устройства после каждого измерения.
Удобным решением является установка параметров контроля температуры и наличия маски в интерфейсе СКУД, если SDK терминала позволяет заносить данные непосредственно в системе.

Количество шаблонов лиц


Ёмкость шаблона максимальное количество наборов данных, которые могут храниться в системе. Чем выше данный показатель, тем выше точность идентификации. Большой ёмкостью распознавания обладают терминалы Face Station 2 и FaceLite. Они обрабатывают до 900 000 шаблонов. Терминалы ProFace X хранят в памяти по 30 000 шаблонов, FaceDepot 7А и Facedepot 7 В по 10 000 шаблонов, SpeedFace V5L 6000.
Терминалы ASI7223X-A и ASI7214X вмещают по 100 000 шаблонов.

Количество пользователей и событий


Количество пользователей в памяти терминала распознавания лиц определяет количество максимально возможных идентификаторов для доступа на объект. Чем крупнее объект, тем выше должен быть данный показатель. Память контроллеров Face Station 2 и FaceLite рассчитана на 30000 пользователей, как и память ProfaceX. FaceDepot 7А, Facedepot 7 В, SpeedFace V5L обрабатывают данные 10 000 человек. Память терминала DS-K1T8105E рассчитана на 1600 пользователей, DS-K1T331 на 3000, DS-K1T606MF на 3200 пользователей. Терминалы ASI7223X-A и ASI7214X обрабатывают данные 100 тысяч пользователей. В памяти терминалов распознавания лиц сохраняются все событиях о проходах через данный терминал. Наибольшее количество событий в памяти позволяет строить отчеты за максимально долгий выбранный период времени.

Самый большой объем журнала событий у терминалов Face Station 2 и FaceLite 5 млн. У ProfaceX 1 млн. Терминалы ASI7223X-A и ASI7214X вмещают по 300 000 событий. Объем журнала SpeedFace V5L составляет 200 000 событий, у DS-K1T331W 150 000. У терминалов FaceDepot 7А и Facedepot 7 В и DS-K1T606MF 100 000 событий. Самый скромный объем памяти у терминала DS-K1T8105E всего 50 000 событий.

Языковой интерфейс


Не все терминалы распознавания лиц, представленные на российском рынке, имеют русскоязычный интерфейс, поэтому его наличие может являться важным фактором выбора.
Русскоязычный интерфейс доступен в терминалах ProFace X, SpeedFace V5L. В терминале Face Station 2 русскоязычная прошивка предоставляется по запросу. Терминал Face Station 2 имеет англоязычный интерфейс. DS-K1T331W поддерживает английский, испанский и арабский языки, русскоязычный интерфейс пока не доступен.

Габариты


Самыми крупными и тяжелыми в нашем обзоре являются терминалы Dahua.
ASI7223X-A 428X129X98 мм, вес 3 кг.
ASI7214X 250,6Х129Х30,5 мм, вес 2 кг.
Далее идет FaceDepot-7A с его весом в 1,5 кг и габаритами 301х152х46 мм.
Самым легким и компактным терминалом в нашем обзоре является Suprema FaceLite его габариты составляют 80x161x72 мм при весе в 0,4 кг.

Габариты терминалов Hikvision:
DS-K1T606MF 281X113X45
DS-K1T8105E 190X157X98
DS-K1T331W 120X110X23

Габариты терминалов Zkteco:
FaceDepot-7B 210X110X14 при весе 0,8 кг
ProfaceX 227X143X26 при весе 1 кг
SpeedFace V5L 203X92X22 при весе 0, 9 кг

Габариты терминала Suprema Face Station 2 141Х164Х125 при весе 0,7 кг.

Характеристики камеры


Терминал Proface X оснащен камерой 2MP WDR Low Light для распознавания лиц в условиях сильного внешнего освещения (50 000 люкс). Терминалы Face Station 2 и FaceLite оборудованы камерой CMOS 720x480 с инфракрасной подсветкой в 25 000 люкс, что позволяет им работать в условиях как низкой, так и высокой освещенности. Данные терминалы могут быть установлены под козырьком на открытом воздухе во избежание сильной засветки. Терминалы Hikvision и Dahua оснащены камерами на 2MP с двойным объективом и WDR, что позволяет получать четкое изображение в условиях различной освещенности. Терминалы FaceDepot 7А, Facedepot 7 В, SpeedFace V5L оснащены камерой
2MP.

Интеграция с турникетами




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

Готовое решение для 3х кейсов по работе с карточками товаров на маркетплейсах

14.05.2021 14:06:19 | Автор: admin

Как повысить эффективность управления карточками товаров и грамотно работать с остатками на маркетплейсах? Этими вопросами задается каждый второй продавец, который хочет занять верхние строчки в поисковой выдаче и увеличить продажи. Разбираемся, какие особенности бывают при работе с карточками товаров, и как их решает RDV Маркет.

Кейс 1: несколько карточек соответствуют 1 товару

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

Решение:

В личном кабинете маркетплейса, возьмем для примера Ozon, продавец заводит две позиции с разными артикулами чехол прозрачный для iPhone 7 и чехол прозрачный для iPhone 8. По факту это один товар, поскольку он подходит для обоих смартфонов.

Несмотря на то, что на маркетплейсе это две разные карточки, в 1С будут учитываться остатки по одному товару. При этом будет трансляция остатков и цен сразу на обе карточки.

Для этого в разделе "RDV:Товарный каталог", в "Управлении товарным предложением" нужно создать связку номенклатур. В этом документе отображается один товар в двух строках, артикул будет совпадать, но будут отличаться идентификаторы в личном кабинете и другие параметры, потому что на маркетплейсе одному товару соответствуют две карточки.

Если у продавца осталось, например, 50 чехлов, то при получении заказов остатки корректируются и транслируются на обе карточки маркетплейса.

Кейс 2: составные товарами и промонаборы

Это актуально тем, кто продает товары, требующие сборки, и/или комплектующие. Остатки по деталям могут использоваться в сборных товарах, часть из которых еще не собрана. Тут есть своя специфика: требуется считать количество возможных составных товаров с учетом остатков по комплектующим и транслировать остатки на маркетплейс.

Решение:

Например, поставщик торгует велосипедами на маркетплейсе Wildberries. В то же время он продает для них комплектующие: рамы, колеса, рули и сидушки. Когда у продавца есть достаточное количество комплектующих, он может собрать еще один велосипед и отправить его на маркетплейс.

Как это можно отобразить в системе 1С? В личном кабинете создаются две позиции: велосипед двухколесный и колесо, по которому передаются остатки.

Данные процессы осуществляются в документе "Управление товарным предложением". В нем указаны позиции велосипеда и шины с различными идентификаторами. На каждую позицию устанавливается цена и передается вместе с остатком.

Если продавец хочет сделать сборку, то в RDV Маркет можно создать варианты комплектации в разделе - "Варианты комплектации номенклатуры". Например, "Велосипед черный сборный", который состоит из трех частей: каркаса, сидушки и двух шин. Мы создаем вариант комплекта с указанием комплектующих - именно это увидит кладовщик в момент сборки. На основании этой информации 1С высчитывает, сколько велосипедов можно собрать.

Далее в отчете "Остатки и доступность товаров для маркетплейсов" продавец может посмотреть детальнее информацию по заказу. Рассмотрим тот же пример - "Велосипед черный сборный", по которому есть остаток на складе 1 шт. При этом 1С пишет, что для заказа доступно 15 шт. Почему так происходит? Все просто - продавец может собрать велосипеды из 3х позиций (каркас, сидушка, шина). И поскольку в наличии есть еще 14 сидушек для велосипеда, то эти данные 1С учитывает и транслирует на маркетплейс цифру 15. Если будет продана отдельно составляющая, например, шина, то остаток пересчитается.

Таким образом, работник склада в АРМ кладовщика увидит, если заказ дополнительно потребует сборки. Для этого нужно перейти в задание на сборку - собрать и отгрузить товар.

Кейс 3: Упаковки и штучные товары

Актуально, когда один товар продается как поштучно, так и упаковками (например, консервы для животных). В этом случае необходимо корректно транслировать остатки в разрезе штук и упаковок на маркетплейсах, а также отображать цены, если у продавца запланированы скидки.

Решение:

Допустим, поставщик продает на Яндекс.Маркете корм для кошек Whiskas в пакетиках. У него есть артикул и цена. Кроме того, у продавца в наличии такой же товар, который поставляется упаковками, состоящими из 24 шт. У упаковки другая цена и артикул. На странице товара на маркетплейсе можно увидеть, что эти товары относятся к одной карточке. Несмотря на это, продавец может транслировать остатки и цены как за одну штуку, так и за упаковку.

Как это реализовано в RDV Маркет? В "Управлении товарным предложением" создается товарное предложение, с помощью которого настраивается обмен по ценам и остаткам. С точки зрения учета у продавца будет один товар влажный корм Whiskas. Разница заключается в том, что он представлен в упаковке по 24 шт. и по 1 шт. В документе отображается один товар в двух строках, артикул совпадает, но отличаются упаковка и идентификаторы в личном кабинете.

Стоимость упаковки в 1С вычисляется автоматически, но при необходимости цену можно изменить. Это можно сделать в разделе "Установка цен товаров": нажать на кнопку "Создать", выбрать нужный маркетплейс и установить цену за одну штуку и упаковку. После проведения документа информация сразу уйдет на маркетплейс.

Таким образом, продавец отображает товар в карточке в двух вариантах, тем самым увеличивая объем продаж. При необходимости можно скрывать штучный товар и продавать его только упаковками.

Описанные кейсы и их решения актуальны для продавцов, работающих по модели FBS. Это связано с тем, что при торговле с собственного склада остатки меняются ежеминутно, поэтому сотрудникам необходимо постоянно обновлять сведения и следить за их актуальностью. В этом как раз помогает RDV Маркет, где учтены все особенности работы с карточками и остатками.

Подробнее..

Интеграция синхронное, асинхронное и реактивное взаимодействие, консистентность и транзакции

25.02.2021 10:21:06 | Автор: admin

Продолжаю серию статей про хорошую интеграцию. В первой статье я говорил, что хорошая админка обеспечит быстрое решение инцидентов как ключевой фактор для устойчивости работы всего комплекса систем. Во второй про использование идемпотентных операций для устойчивой работы в условиях асинхронного взаимодействия и при сбоях. В этой статье рассмотрим синхронный, асинхронный и реактивный способы взаимодействия между сервисами и более крупными модулями. А так же способы обеспечить консистентность данных и организовать транзакции.

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

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

Синхронное взаимодействие

Синхронное взаимодействие самое простое. Оно скрывает все детали удаленного вызова, что для вызывающего сервиса превращается в обычный вызов функции с получением ответа. Для его организации есть множество протоколов например, давно известные RPC и SOAP. Но очевидная проблема синхронности в том, что удаленный сервис может отвечать не очень быстро даже при простой операции на время ответа влияет загруженность сетевой инфраструктуры, а также другие факторы. И все это время вызывающий сервис находится в режиме ожидания, блокируя память и другие ресурсы (хотя и не потребляя процессор). В свою очередь, блокированные ресурсы могут останавливать работу других экземпляров сервиса по обработке сообщений, замедляя тем самым уже весь поток обработки. А если в момент обращения к внешнему сервису у нас есть незавершенная транзакция в базе данных, которая держит блокировки в БД, мы можем получить каскадное распространение блокировок.

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

При этом стандартный шаблон работы с базой данных - все изменения по одному запросу пользователя проводить в одной транзакции (разумно их делить), а не завершать транзакции после каждого оператора для обеспечения консистентности данных. Поэтому при выносе хранения товаров в отдельный сервис нам не просто надо переписать процедуры запроса атрибутов на обращения к сервису, а провести реинжиниринг кода: сначала запросить все необходимые данные от других сервисов, а потом начать делать изменения в базе данных.

Казалось бы, это всё очевидно. Но я встречался со случаями, когда синхронные вызовы ставили без необходимости, искренне не понимая, что выполнение будет долгим, а накладные расходы большими. Отдельная засада заключается в том, что современные системы разработки позволяют вынести сервисы на удаленный сервер не меняя исходного кода, и сделать это могут администраторы при конфигурировании системы. В том числе на уровне базы данных я встречался с идеями, когда централизованное хранение логов на полном серьезе предлагали делать просто за счет переноса локальных таблиц на общий сервис так, чтобы прямые вставки в них превратились в ставки по dblink. Да, это простое решение. Только очень неустойчивое по производительности и чувствительное к сбоям сетевой инфраструктуры.

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

И третья проблема связана с масштабированием. При синхронном взаимодействии один экземпляр вызывающего сервиса вызывает один экземпляр вызываемого, но который, в свою очередь, тоже может вызывать другие сервисы. И нам приходится существенно ограничивать возможность простого масштабирования через увеличение экземпляров запущенных сервисов, при этом мы должны проводить это масштабирование сразу по всей инфраструктуре, поддерживая примерно одинаковое число запущенных сервисов с соответствующей затратой ресурсов, даже если проблема производительности у нас только в одном месте.

Поэтому синхронное взаимодействие между сервисами и системами зло. Оно ест ресурсы, мешает масштабированию, порождает блокировки и взаимное влияние разных серверов.

Я бы рекомендовал избегать его совсем, но, оказывается, есть одно место, в котором протокол поддерживает только синхронное взаимодействие. А именно взаимодействие между сервером приложений и базой данных по JDBC синхронно принципиально. И только некоторые NoSQL базы данных поддерживают реально асинхронное взаимодействие со стороны сервера приложений и вызовы callback по результату обработки. Хотя казалось бы, мы находимся в поле бэкенд-разработки, которая в наше время должна быть ориентирована на асинхронное взаимодействие... Но нет и это печально.

Транзакции и консистентность

Раз уж зашла речь про базы данных, поговорим о транзакционности работы. Там более, что именно она часто является аргументом за синхронное взаимодействие.

Начнем с того, что транзакционность была в свое время громадным преимуществом реляционных баз данных и снимала с разработчиков громадное количество работы по обработке ошибок. Вы могли рассчитывать, что все изменения, которые бизнес-логика выполняет в базе данных при обработке одного запроса пользователя либо будут целиком зафиксированы, либо целиком отменены (если при обработке произошла ошибка), а база данных вернется в прежнее состояние. Это обеспечивал механизм транзакций реляционной базы данных и паттерн UnitOfWork в приложении-клиенте или на сервере приложений.

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

Появление трехзвенной архитектуры и сервера приложений принципиально не изменило ситуацию. Если каждый запрос пользователя обрабатывается сервером приложений в одном вызове, то заботу о консистентности вполне можно возложить на транзакции базы данных. Это тоже было типовым шаблоном реализации.

Когда же пришла пора распределенных систем, то это преимущество решили сохранить. Особенно на уровне базы данных потому что возможность вынести часть хранения на другой сервер средствами администратора представлялась крайне желанной. В результате появились распределенные транзакции и сложный протокол двухфазного завершения, который призван обеспечить консистентность данных в случае распределенного хранения.

Призван, но по факту не гарантирует. Оказывается, в случае сбоев даже промышленные системы межсистемных транзакций, такие, как взаимодействие по Oracle dblink, могут привести к тому, что в одной из систем транзакция будет завершена, а в другой нет. Конечно, это тот самый исчезающе маловероятный случай, когда сбой произошел в крайне неудачный момент. Но при большом количестве транзакций это вполне реально.

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

Вообще, расчет на штатные средства резервирования базы данных в распределенном IT-ландшафте иногда играет злую шутку. Очень печальная история восстановления после аварии произошла, когда основной сервер одной из систем деградировал по производительности. Причина проблем была совершенно неясна, не исключены были даже проблемы на уровне железа, поэтому решили переключиться на standby в конце концов, его именно для таких случаев и заводили. Переключились. После этого выяснилось, что потерялись несколько последних минут работы, но в корпоративной системе это не проблема пользователей оповестили, они работу повторили.

Проблема оказалась в другом: смежные системы, с которыми взаимодействовал сервер, оказались совершенно не готовы к откату его состояния на несколько минут они же уже обращались и получили успешный ответ. Ситуация усугубилась тем, что об этом просто не подумали при переключении, а проблемы начали проявляться не сразу поэтому эффект оказался довольно большим, и его пришлось несколько дней искать и устранять сложными скриптами.

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

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

Поэтому использование транзакций базы данных и встроенных механизмов, в общем случае, не будет гарантией консистентности данных и устойчивости работы комплекса систем. А вот деградацию производительности из-за межсистемных блокировок вы точно получите.

Когда же объектно-ориентированный подход сменил процедурную парадигму разработки, мы получили еще одну проблему. Для поддержки работы с персистентными объектами на уровне сервера приложений были разработаны объектно-реляционные мапперы (ORM). Но тут-то и выяснилось, что шаблон UnitOfWork и возможность отката транзакций концептуально противоречат ORM каждый объект инкапсулирует и сложность, и логику работы, и собственные данные. Включая активное кэширование этих данных, в том числе и между сессиями разных пользователей для повышения производительности при работе с общими справочниками. А отката транзакций в памяти на уровне сервера приложений не предусмотрено.

Конечно, на уровне ORM или в надстройке над ним можно вести списки измененных объектов, сбрасывать их при завершении UnitOfWork, а для отката считывать состояние из базы данных. Но это возможно, когда вся работа идет только через ORM, а внутри базы данных нет никакой собственной бизнес-логики, изменяющей данные, например, триггеров.

Может возникнуть вопрос а какое все это имеет отношение к интеграции, это же проблемы разработки приложения как такового? Это, было бы так, если бы многие legacy-системы не выставляли API интеграции именно на уровне базы данных и не реализовывали логику на этом же уровне. А это уже имеет прямое отношение к интеграции в распределенном IT-ландшафте.

Замечу, что взаимодействие между базами данных тоже не обязательно должно быть синхронным. Тот же Oracle имеет различные библиотеки, которые позволяют организовывать асинхронное взаимодействие между узлами распределенной базы данных. И появились они очень давно мы успешно использовали асинхронное взаимодействие в распределенной АБС Банка еще в 1997 году, даже при скорости канала между городами, по которому шло взаимодействие, всего 64К на всех пользователей интернета (а не только нашей системы).

Асинхронное и реактивное взаимодействие

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

Для получения ответа есть два основных способа:

  • обычное асинхронное взаимодействие, когда передающая система сама периодически опрашивает состояние документа;

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

Оба способа вы можете увидеть на схеме вместе с очередями и логикой обработки, которая при этом возникает.

Какой именно способ использовать зависит от способа связи. Когда канал однонаправленный, как при обычном клиент-серверном взаимодействии или http-протоколе, то клиент может запросить сервер, а вот сервер не может обратиться к клиенту взаимодействие получается асинхронным.

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

Реактивное взаимодействие требует определенной перестройки мышления, которая не столь проста, как кажется, потому что есть желание не просто упростить запись, а скрыть реактивное программирование и писать в традиционном стиле. Впервые я это осознал, когда был в 2014 году на конференции GoToCon в Копенгагене (мой отчет) и там же услышал про Реактивный манифест (The Reactive Manifesto). Там как раз обсуждалось создание различных библиотек, поддерживающих эту парадигму взаимодействия, потому что она позволяет гибко работать с производительностью. Сейчас это встроено в ряд языков через конструкции async/await, а не просто в библиотеки.

Но фишка в том, что такое скрытие усложняет понимание происходящего внутри приложения. Засада происходит в том случае, когда к объектам, обрабатываемым в таком асинхронном коде, обращаются из других мест например, они могут быть возвращены в виде коллекций, запрашивающих объекты. И если не позаботиться специально, то вполне могут быть ситуации одновременного изменения объектов. Вернее, псевдо-одновременного между двумя асинхронными вызовами объект изменяется, хотя с точки зрения разработчика мы как бы находимся внутри потока управления одной процедуры.

Впрочем, шаблоны реактивного программирования это отдельная тема. Я же хочу заострить внимание на том, что в реактивном взаимодействии есть не только переключение потоков, но и скрытые очереди. А скрытые очереди хуже явных, потому что когда возникает дефицит ресурсов и возрастает нагрузка, все тайное становится явным. Это, конечно, не повод усложнять запись, но, как и в случае с автоматическим управлением памятью, эти механизмы надо понимать и учитывать. Особенно в интеграции, когда это используется для взаимодействия между узлами и сервисами, которые потенциально находятся на разных узлах.

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

  • Проектирующий систему аналитик, должен понимать, какие будут последствия для пользователей, и как именно с ними разбираться что нужно автоматизировать, а что может решить служба поддержки.

  • При этом аналитик плотно взаимодействует с разработчиком, который смотря на соответствие этой схемы и фактической реализации, может указать на разные проблемы и предложить их решение.

  • А третий человек тестировщик. Он должен придумать, как проверить, что в случае сбоев и падений отдельных сервисов система ведет себя именно так, как задумано что не возникает документов в промежуточных состояниях и которые не видны ни на интерфейсах ни службе поддержки; что отсутствует случайная двойная обработка документа и так далее.

Поясню эти задачи на примерах. Пусть один сервис обрабатывает заказы от покупателей, а другой резервирует товары по этим заказам на остатках. Задача в этом случае несмотря на падения серверов или сбои связи, не должно быть ситуации, когда по заказу на 3 единицы они зарезервировались дважды, то есть заблокировалось 6 единиц, потому что первый раз сервис резервирования его выполнил, а квитанцию о резерве не послал или сервис обработки заказов не смог эту квитанцию обработать.

Другой пример в дата-центре установлен сервис отправки чеков в ФНС, которая выполняется через взаимодействие со специализированным оборудованием ККМ. И тут тоже надо обеспечить, чтобы каждый чек был отправлен в налоговую ровно один раз, при том, что сама ККМ может работать ненадежно и со сбоями. А в тех случаях, когда алгоритм не может однозначно выяснить результат обработки чека, о появлении такой ситуации должна быть оповещена служба поддержки для разбора. Это должны спроектировать аналитики с разработчиками, а тестировщики проверить при различных сбойных ситуациях. И лучше это делать автоматически, особенно если в интеграции есть сложные алгоритмы обработки.

Конечно, никакая проверка не дает гарантий. Поэтому при межсистемном взаимодействии важно вести журналы с обеих сторон (хотя при большом объеме приходится думать, как чистить или архивировать логи). Казалось бы, это очевидно. Но нет я встречал много систем, где журналы отсутствовали и диагностировать проблемные ситуации было невозможно. И много раз доказывал при проектировании, что журналирование обмена необходимо.

Консистентность данных

Вернемся к вопросам консистентности данных при асинхронном взаимодействии. Как его обеспечивать, если транзакции недоступны? В прошлой статье я предложил одно из решений шаблон идемпотентных операций. Он позволяет в случае проблем с получением ответа в обработке сообщения просто отправить его повторно, и оно при этом будет обработано корректно. Этот же прием можно применять в ряде ситуативных технических ошибок, например, при отказе из-за блокировок на сервере.

Если же вы получили содержательную ошибку, например, отказ в отгрузке уже зарезервированного и оплаченного заказа, то, скорее всего, с такими проблемами надо разбираться на бизнес-уровне, никакие транзакции тут не помогут. В самом деле, отказ отгрузки может в том числе означать, что зарезервированный товар присутствовал только в остатках информационной системы, а на реальном складе его не было. То есть, его действительно не получится отправить покупателю, а значит, разбираться с этим надо на уровне бизнес-процесса, а не технически. По моему опыту, подобные ситуации достаточно распространены многие разработчики почему-то забывают, что ИТ-системы лишь отражают реальный мир (и отражение может быть неверным) и сосредотачиваются на техническом уровне.

Организация транзакций

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

В основе транзакционной работы лежит необходимость персистентного сохранения в базу данных, при этом транзакции и кванты обработки стоит настраивать отдельно, исходя из требований производительности. По моему опыту, если у вас идет массовая загрузка или передача данных, то вовсе не обязательно оптимальным будет commit на каждую запись всегда есть некоторое оптимальное количество. При этом внутри процедуры загрузки можно ставить savepoint с перехватом ошибок для каждой отдельной записи, чтобы откатывать не всю порцию загрузки, а лишь обработку ошибочных. Только помните, что откатываются только изменения в таблицах, а некоторые другие вещи могут не откатываться, например, работа с переменными пакетов в Oracle или некоторые способы постановки в очереди или работа с файлами. Но это и при обычном rollback сохраняется.

Еще надо понимать, что при передаче документов со строками (например, больших накладных) принимающая система далеко не всегда работает корректно, если документ был обработан частично (даже если у строк есть отдельный атрибут-статус). При этом, что интересно, передающая система, наоборот, может не обеспечивать целостность обработки документов. И тогда надо принимать специальные меры для обеспечения согласованности.

У меня было два подобных кейса. В одном случае требовалось в принимающей системе снять ограничение, связанное с долгим проведением больших документов, потому что некоторые накладные включали тысячи позиций и обычный режим исполнения (целиком) приводил к долгим блокировкам. В этом случае для построчной обработки мы создали отдельную технологическую таблицу со статусами строк и очень аккуратно работали со статусом самого документа.

В другом случае складская система обрабатывала документы отгрузки наоборот, построчно, выдавая общий поток ответов по всем строкам всех входящих документов. Тогда как передающая система рассчитывала, что на каждый переданный документ придет целостный ответ. В этом случае нам пришлось создать специальный промежуточный сервис, который на входе запоминал, сколько строк имеет каждый передаваемый документ, фиксировал полученные ответы и передавал их обратно в отправляющую систему только когда по всем строкам получал ответ. Он также поднимал тревогу, когда ответ по некоторым строкам слишком долго задерживался. Эвристики для этого алгоритма мы прорабатывали отдельно, так как за формированием ответа стояла реальная обработка позиций документа людьми, которая, естественно, была далеко не мгновенной.

На этом я завершаю третью статью про интеграцию. Надеюсь, она помогла вам разобраться в различных вариантах взаимодействия систем, и это окажется полезным при проектировании, разработке и тестировании.

С 1 февраля стоимость очного участия в DevOpsConf 2021 составит 36000 рублей. Забронируйте билет сейчас, и у вас будет ещё несколько дней на оплату.

На данный момент Программный комитет одобрил уже около 40 докладов, но до 28 февраля ещё принимает заявки. Если вы хотите быть спикером, то подать доклад можно здесь.

Чтобы быть на связи, подписывайтесь на наши соцсети, чтобы не упустить важные новости о конференции и наших онлайн-событиях VK, FB, Twitter, Telegram-канал, Telegram-чат.

Подробнее..

Перевод Код-ревью в IDE интеграция между Space и IntelliJ IDEA 2021.1

08.04.2021 20:08:42 | Автор: admin

Привет, Хабр!

Вчера вышло обновление IntelliJ IDEA 2021.1. В него вошла интеграция с JetBrains Space, которая позволяет использовать любую IDE на платформе IntelliJ для код-ревью: назначать ревью и управлять ими, просматривать и добавлять комментарии, принимать изменения. Как это работает, мы подробно расскажем в этом посте.

Space предоставляет командам разработчиков удобный способ проверять код друг друга и обсуждать изменения. После внесения изменений вы можете попросить одного или нескольких коллег выполнить ревью вашего кода. При работе над функциональной веткой ее можно заново объединить с базовой веткой прямо из Space.

Доступ к код-ревью и мердж-реквестам можно получить из браузера и десктопного приложения Space. А теперь также из IDE! Нативная интеграция между Space и нашими IDE на базе IntelliJ открывает много новых возможностей, и код-ревью это только первый шаг. Мы планируем развивать и улучшать эту интеграцию.

Видео

Если вы предпочитаете видео, смотрите обзор этой функциональности от Триши Ги:

Space-плагин встроен в свежую версию IntelliJ IDEA 2021.1. Для других наших IDE плагин нужно установить вручную.

Прежде чем приступить к ревью кода, войдите в Space из IDE. Это можно сделать в настройках: Tools | Space. Введите URL-адрес своей организации Space, нажмите Log In, и ваш дефолтный браузер попросит вас предоставить доступ из IDE.

После этого в Get from VCS появится список всех проектов и репозиториев в вашей организации Space. Найдите и выберите Git-репозиторий, с которого вы хотите начать, и нажмите Clone.

У Space-плагина есть свое окно, в котором можно просматривать задания в Space Automation. Плагин также обеспечивает автодополнение и подсветку синтаксиса для файлов .space.kts.

Но мы здесь из-за код-ревью, так что перейдем к делу.

Окно Code Reviews

Окно Space Code Reviews открывается с боковой панели или через меню Tools | Space | Code Reviews. В нем вы увидите все ревью, относящиеся к текущему проекту. В окне работает поиск и фильтры.

Фильтры помогут отсортировать:

  • Открытые и закрытые ревью;

  • Ревью, в которых содержатся ваши изменения;

  • Ревью, требующие вашего внимания;

  • Изменения, которые вам нужно просмотреть;

  • Ревью, назначенные на вас.

Хронология код-ревью

Из списка ревью можно перейти к информации о каждом из них. Вы увидите основные сведения о ревью, например, кто является автором и ревьюером. Вы можете сделать checkout ветки мердж-реквеста, если хотите, чтобы код был у вас под рукой.

Также вы увидите хронологию ревью со всеми комментариями и изменениями. Вы можете отвечать на комментарии, добавлять свои, удалять решенные вопросы.

В Space важное место занимают чаты. Комментарии к код-ревью это тоже чат: оставляйте дополнительные комментарии и отвечайте коллегам в тредах. Все это не выходя из IDE.

Коллеги, использующие браузерное или десктопное приложение Space, увидят ваши комментарии прямо в ревью, если оно открыто, либо в чате.

Ревью кода в IDE

Открыв ревью в IDE, вы увидите список файлов, которые были добавлены, изменены или удалены. Вы можете открыть их и посмотреть, что изменилось, в интерфейсе просмотра различий. В нем можно оставлять комментарии к любым строкам кода.

Комментарии сохраняются как черновик и публикуются, только когда вы завершите код-ревью. При необходимости используйте опцию Post now, чтобы сразу же отправить комментарий.

Принять изменения или дождаться ответа

Когда вы закончите анализировать изменения, можете завершить свой этап код-ревью. На вкладке Details вы можете выбрать:

  • Accept Changes, то есть принять изменения, если на ваш взгляд с кодом все в порядке.

  • Wait for Response, то есть подождать ответа, если вы ознакомились с изменениями, но у вас остались вопросы или в коде есть нерешенные проблемы.

В любом случае ваш этап проверки будет завершен и ваши комментарии будут опубликованы.

Заключение

Команды выполняют ревью кода по многим причинам. Коллеги могут проверять, насколько понятен новый код, легко ли его читать, нет ли в нем серьезных недостатков, прежде чем одобрить слияние с основной веткой. Теперь все это можно делать прямо в IDE.

Space-плагин встроен в IntelliJ IDEA 2021.1, а для других наших IDE его можно установить вручную.

Вы можете бесплатно зарегистрироваться в Space и легко создать зеркало существующего Git-репозитория, чтобы пользоваться всеми возможностями Space для код-ревью в IntelliJ IDEA.

Конечно же, в дальнейшем мы будем расширять функциональность плагина. Что бы вы хотели в нем увидеть? Расскажите нам об этом в комментариях.

Подробнее..

Перевод Бесшовная интеграция Microsoft Excel и Word с помощью Python

22.04.2021 16:18:43 | Автор: admin

Хотя в среднем для каждодневных задач автоматизация не требуется, бывают случаи, когда она может быть необходима.Создание множества диаграмм, рисунков, таблиц и отчётов может утомить, если вы работаете вручную. Так быть не должно.Можно построить конвейер на Python, с помощью которого Excel и Word легко интегрировать: нужно создать таблицы в Excel, а затем перенести результаты в Word, чтобы практически мгновенно получить отчёт.


Openpyxl

Встречайте Openpyxl возможно, одну из самых универсальных связок [биндингов] с Python, которая сделает взаимодействие с Excel очень простым. Вооружившись этой библиотекой, вы сможете читать и записывать все нынешние и устаревшие форматы Excel, то есть xlsx и xls.

Openpyxl позволяет заполнять строки и столбцы, выполнять формулы, создавать 2D и 3D диаграммы, маркировать оси и заголовки, а также предоставляет множество других возможностей, которые могут пригодиться.

И самое важное этот пакет позволяет вам перебирать бесконечное количество строк и столбцов в Excel, тем самым избавляя вас от всех этих надоедливых вычислений и построения графиков, которые вам приходилось делать раньше.

Python-docx

Затем идёт Python-docx, этот пакет для Word то же самое, что Openpyxl для Excel. Если вы ещё не изучили его документацию, вам, вероятно, стоит взглянуть на неё. Python-docx без преувеличения один из самых простых и понятных мне наборов инструментов, с которыми я работал с тех пор, как начал работать с самим Python.

Python-docx позволяет автоматизировать создание документов путём автоматической вставки текста, заполнения таблиц и рендеринга изображений в отчёт без каких-либо накладных расходов. Без лишних слов давайте создадим наш собственный автоматизированный конвейер. Запустите Anaconda (или любую другую IDE по вашему выбору) и установите эти пакеты:

pip install openpyxlpip install python-docx

Автоматизация Microsoft Excel

Сначала загрузим уже созданный лист Excel, вот так:

workbook = xl.load_workbook('Book1.xlsx')sheet_1 = workbook['Sheet1']

Теперь переберём все строки в нашей таблице, чтобы вычислить и вставить значения мощности, умножив ток на напряжение:

for row in range(2, sheet_1.max_row + 1):    current = sheet_1.cell(row, 2)    voltage = sheet_1.cell(row, 3)    power = float(current.value) * float(voltage.value)    power_cell = sheet_1.cell(row, 1)    power_cell.value = power

Как только это будет сделано, мы используем рассчитанные значения мощности, чтобы сгенерировать линейную диаграмму, которая будет вставлена в указанную ячейку, код показан ниже:

values = Reference(sheet_1, min_row = 2, max_row = sheet_1.max_row, min_col = 1, max_col = 1)chart = LineChart()chart.y_axis.title = 'Power'chart.x_axis.title = 'Index'chart.add_data(values)sheet_1.add_chart(chart, 'e2') workbook.save('Book1.xlsx')
Автоматически созданная таблица ExcelАвтоматически созданная таблица Excel

Извлечение диаграммы

Теперь, когда мы сгенерировали нашу диаграмму, нам нужно извлечь её как изображение, чтобы мы могли использовать её в нашем отчёте Word. Сначала укажем точное местоположение файла Excel, а также место, где должно быть сохранено изображение диаграммы:

input_file = "C:/Users/.../Book1.xlsx"output_image = "C:/Users/.../chart.png"

Затем откройте электронную таблицу, используя следующий метод:

operation = win32com.client.Dispatch("Excel.Application")operation.Visible = 0operation.DisplayAlerts = 0workbook_2 = operation.Workbooks.Open(input_file)sheet_2 = operation.Sheets(1)

Позднее вы сможете перебирать все объекты диаграммы в электронной таблице (если их несколько) и сохранять их в указанном месте:

for x, chart in enumerate(sheet_2.Shapes):    chart.Copy()    image = ImageGrab.grabclipboard()    image.save(output_image, 'png')    passworkbook_2.Close(True)operation.Quit()

Автоматизация Microsoft Word

Теперь, когда у нас есть сгенерированное изображение диаграммы, мы должны создать шаблон документа, который в принципе является обычным документом Microsoft Word (.docx), сформированным именно так, как мы хотим: отчёт содержит шрифты, размеры шрифтов, структуру и форматирование страниц.

Теперь всё, что нам нужно сделать, создать плейсхолдеры для сгенерированного нами контента, то есть табличные значения и изображения, и объявить их с именами переменных, как показано ниже.

Шаблон документа Microsoft WordШаблон документа Microsoft Word

Любой сгенерированный контент, включая текст и изображения, может быть объявлен в двойных фигурных скобках {{ variable_name }}. В случае таблиц вам нужно создать таблицу со строкой шаблона со всеми включёнными столбцами, затем нужно добавить одну строку вверху и одну строку ниже со следующей нотацией:

Первая строка:

{%tr for item in variable_name %}

Последняя строка:

{%tr for item in variable_name %}

На рисунке выше имена переменных:

  • table_contents для словаря Python, в котором будут храниться наши табличные данные;

  • Index для ключей словаря (первый столбец);

  • Power, Current и Voltage для значений словаря (второй, третий и четвёртый столбцы).

Затем импортируем наш шаблонный документ в Python и создаём словарь, в котором будут храниться значения нашей таблицы:

template = DocxTemplate('template.docx')table_contents = []for i in range(2, sheet_1.max_row + 1):    table_contents.append({        'Index': i-1,        'Power': sheet_1.cell(i, 1).value,        'Current': sheet_1.cell(i, 2).value,        'Voltage': sheet_1.cell(i, 3).value        })

Далее импортируем ранее созданное в Excel изображение диаграммы и создадим другой словарь для создания экземпляров всех объявленных в документе шаблона переменных-заполнителей:

image = InlineImage(template,'chart.png',Cm(10))context = {    'title': 'Automated Report',    'day': datetime.datetime.now().strftime('%d'),    'month': datetime.datetime.now().strftime('%b'),    'year': datetime.datetime.now().strftime('%Y'),    'table_contents': table_contents,    'image': image    }

И, наконец, визуализируем отчёт с нашей таблицей значений и изображением диаграммы:

template.render(context)template.save('Automated_report.docx')

Результаты

И вот автоматически сгенерированный отчёт Microsoft Word с числами и созданной в Microsoft Excel диаграммой. Мы получили полностью автоматизированный конвейер, его можно использовать, чтобы создать столько таблиц, диаграмм и документов, сколько вам потребуется.

Автоматически сгенерированный отчётАвтоматически сгенерированный отчёт

Исходный код

import openpyxl as xlfrom openpyxl.chart import LineChart, Referenceimport win32com.clientimport PILfrom PIL import ImageGrab, Imageimport osimport sysfrom docx.shared import Cmfrom docxtpl import DocxTemplate, InlineImagefrom docx.shared import Cm, Inches, Mm, Emuimport randomimport datetimeimport matplotlib.pyplot as plt######## Generate automated excel workbook ########workbook = xl.load_workbook('Book1.xlsx')sheet_1 = workbook['Sheet1']  for row in range(2, sheet_1.max_row + 1):    current = sheet_1.cell(row, 2)    voltage = sheet_1.cell(row, 3)    power = float(current.value) * float(voltage.value)    power_cell = sheet_1.cell(row, 1)    power_cell.value = power  values = Reference(sheet_1, min_row = 2, max_row = sheet_1.max_row, min_col = 1, max_col = 1)chart = LineChart()chart.y_axis.title = 'Power'chart.x_axis.title = 'Index'chart.add_data(values)sheet_1.add_chart(chart, 'e2')  workbook.save('Book1.xlsx')######## Extract chart image from Excel workbook ########input_file = "C:/Users/.../Book1.xlsx"output_image = "C:/Users/.../chart.png"operation = win32com.client.Dispatch("Excel.Application")operation.Visible = 0operation.DisplayAlerts = 0    workbook_2 = operation.Workbooks.Open(input_file)sheet_2 = operation.Sheets(1)    for x, chart in enumerate(sheet_2.Shapes):    chart.Copy()    image = ImageGrab.grabclipboard()    image.save(output_image, 'png')    passworkbook_2.Close(True)operation.Quit()######## Generating automated word document ########template = DocxTemplate('template.docx')#Generate list of random valuestable_contents = []for i in range(2, sheet_1.max_row + 1):        table_contents.append({        'Index': i-1,        'Power': sheet_1.cell(i, 1).value,        'Current': sheet_1.cell(i, 2).value,        'Voltage': sheet_1.cell(i, 3).value        })#Import saved figureimage = InlineImage(template,'chart.png',Cm(10))#Declare template variablescontext = {    'title': 'Automated Report',    'day': datetime.datetime.now().strftime('%d'),    'month': datetime.datetime.now().strftime('%b'),    'year': datetime.datetime.now().strftime('%Y'),    'table_contents': table_contents,    'image': image    }#Render automated reporttemplate.render(context)template.save('Automated_report.docx')

Вот мой репозиторий на GitHub с шаблоном документа и исходным кодом для этого туториала. А вот ссылка на курс Fullstack-разработчик на Python, который сделает из вас настоящего универсального солдата от кодинга.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Digital-трансформация завода CRM для ERP, роботизация БП и оживление железа, ЛК, чат-боты и dream team (ч. 1)

18.01.2021 12:13:17 | Автор: admin

Часть 1: CRM для ERP (в этой публикации)

Часть 2: Роботизация бизнес-процессов и оживление железа

Часть 3: Личные кабинеты, чат-боты и dream team

Текущий уровень автоматизации: 2 года назад

Ситуация на момент моего прихода в компанию на должность CIO:

  • Коротко о заводе: круглосуточное производство, круглосуточная отгрузка продукции

  • Необновляемая с 2014 года ERP 2 и платформа 1С, пресс-релиз о внедрении

  • Бизнес-процессы компании, оторванные от бизнес-процессов в ERP, и как следствие:

    • Параллельная вселенная в виде файлов Excel, Google-таблиц и облачных хранилищ

    • Присутствие журналов и реестров для ручного заполнения сотрудниками

    • Много бумажного документооборот, в т.ч. с контрагентами

  • Минимум данных в ERP-системе и исторические "костыли" в разработке

  • Скромный сайт компании без какого-либо функционала и интеграций

  • Штат ИТ не укомплектован, процессы в дирекции толком не выстроены

  • Огромный список "зависших" текущих задач, начиная с 2016 года

  • Поддержка: 2 сисадмина и 1 помощник, консультант 1С и консультант-программист начального уровня на ГПХ

  • Один договор с одной местной компанией "1С:Франчайзи" на оказание услуг

  • Авральный режим работы пользователей и бесконечное "тушение" пожаров службой ИТ

  • Большое желание руководства компании выйти на новый уровень автоматизации

Первая smart-задача: "сделать CRM за 3 месяца!"

Мои попытки уточнить детали задачи, ее рамки и требуемые результаты - не увенчались успехом. В коммерческой дирекции ассоциации с CRM были весьма размытые, опыта никто не имел.

К счастью, с CRM-системами я был хорошо знаком

Еще с 2016 года, когда я был руководителем проектов внедрений в ИТ-компании, у меня уже были клиенты, которым мы продавали и настраивали 1C:CRM.

В 2018 году я возглавил новое направление в этой же ИТ-компании по внедрению популярных CRM-систем (1C:CRM, amoCRM, Битрикс24.CRM).

Личный опыт придал мне сил и уверенности выполнить задачу в сжатые сроки. А скромный ИТ-бюджет, доставшийся по-наследству, только добавлял драйва и азарта.

"Коробочная" CRM для исторической ERP: это реально!

Неожиданная новость: у нас уже заключен договор на разработку CRM

За 2 месяца до прихода в компанию, мой предшественник заключил договор на разработку CRM-системы (именно разработку новой CRM "с нуля", а не внедрения существующих).

Ко мне на встречу пришел программист 1C, который выполнял разработку CRM-системы для нашей ERP, чтобы сдать проект и подписать акт.

Тщательный аудит и проверка работ на соответствие ТЗ показала, что это была попытка "срубить бабла". Фактически в ERP была включена функциональная опция встроенной подсистемы CRM (набивалка) и настроены статусы документа "Сделка" в качестве "воронки" продаж.

Самое интересное, что ТЗ было написано подрядчиком и весьма не плохо, но до разработки так и не дошло.

Договор был расторгнут по причине невыполнения ТЗ.

Далее по совокупности критериев, здравого смыла, и сохранения режима "одного окна" в ERP для менеджеров по продажам - принято решение встроить "коробочный" модуль 1C:CRM 3.

Первая сложность: актуальный модуль 1C:CRM 3 подходил только для ERP 2.4, а у нас редакция 2.1.

Вторая сложность: историческая ERP сильно кастомизирована, местами очень "криво".

Третья сложность: никто из подрядчиков не соглашался выполнять эту задачу, по вышеуказанным причинам.

Лайфхак: Facebook спешит на помощь

Мой 12-летний опыт внедрения различных конфигураций 1С говорил о том, что эти сложности решаемые, и модуль можно интегрировать с ERP.

Хотя подобных кейсов в интернете и на технических форумах я не нашел, обратился к разработчикам через партнерскую группу в Facebook:

  • Разработчики честно ответили, что на их практике такого не было, но гипотетически это возможно, если перенести часть БСП из ERP 2.4 в ERP 2.1 (после чего попробовать интегрировать сам модуль).

  • Один из партнеров с Украины ответил положительно. На их практике было 2 успешных внедрения, когда они встраивали модуль 1C:CRM 3, только в ERP 2.2.

Скриншоты переписки из Facebook я отправил по email потенциальным подрядчикам, и один из них согласился выполнить работу.

Через месяц модуль 1С:CRM 3 был встроен в нашу ERP 2.1.

Еще месяц ушел на интеграцию CRM с почтой, офисной АТС в "облаке" и сервисом отправки SMS.

Еще около месяца ушло настройку новой подсистемы, "воронки" продаж, портрета клиента, обучения менеджеров по продажам.

Smart-задача выполнена в срок.

После запуска в эксплуатацию, CRM получила развитие:
  • Новый сайт компании, интегрированный с ERP (подробнее во второй части)

  • Интеграция ERP с Wialon и ЭТРАН

  • SMS-робот и Email-робот

Ноу-Хау: Самый краткий CRM-отчет для оценки KPI

Отчет учитывает только факты, в основе - только данные. Когда руководителю нужны детали, по каждой строке есть расшифровка, вплоть до прослушивания звонков, прочтения писем и задач в истории сделок.

Отчет в CRM для оценки KPI менеджеров по продажамОтчет в CRM для оценки KPI менеджеров по продажам
  1. По данным ERP

  2. По данным звонков и писем с контактным лицам клиентов в CRM

  3. По данным CRM

  4. По данным звонков, сайта и почты в CRM

  5. По данным ERP

  6. По данным ERP

  7. По данным CRM

  8. По данным CRM

Внимание!

С появлением этого отчета, менеджеры тщательно проверяли в нем данные, чтобы не был потерян ни один звонок, ни одно письмо.

Этот отчет мотивирует менеджера по продажам:

- Поддерживать актуальную контактную информацию по всем клиентам.

- Звонить и вести переписку с клиентами с корп. SIM и корп. почты.

- Добиться от ИТ, чтобы CRM работала корректно.

Факты: Фиксация всех входящих и исходящих звонков в ВАТС и CRM

Связка "ВАТС + номер 8-800 + корп.SIM + Софтфон + CRM" иногда сбоила и некоторые звонки не фиксировались. Для решения всех проблем с корп.SIM и номером 8-800 потребовалось около трех месяцев плотной работы с саппортом Мегафон и Рарус.

Мы добились выпуска нескольких обновлений и 100% фиксации звонков в CRM.

Все звонки в ВАТС (8-800, корп. SIM, офисная АТС)Все звонки в ВАТС (8-800, корп. SIM, офисная АТС)Все звонки в CRM (входящие, исходящие, пропущенные)Все звонки в CRM (входящие, исходящие, пропущенные)
Пример: Воронка продаж, портрет клиента, потенциальные сделки

Основные инструменты менеджера по продажам в CRM для работы с клиентами:

Портрет клиента в CRM (детальный)Портрет клиента в CRM (детальный)Воронка продаж в CRM (канбан менеджера)Воронка продаж в CRM (канбан менеджера)Потенциальная сделка в CRM (история взаимодействий)Потенциальная сделка в CRM (история взаимодействий)

Мы также нашли способ автоматической фиксации фактов встреч менеджеров с клиентами в CRM по данным GPS и мобильной сети (не реализовали только из-за отсутствия полноценного API со стороны сервисов, а ручной режим нам не нужен).

Ниже пример стандартных отчетов в CRM:

Анализ источников привлечения клиентов
Анализ источников привлечения в CRMАнализ источников привлечения в CRM
Причины потерь потенциальных сделок (клиентов)
Анализ потерь интересов в CRMАнализ потерь интересов в CRM

SMS-робот и Email-робот для автоматических уведомлений

Модуль 1C:CRM для ERP уже содержал в себе функционал для подключения к SMS-провайдеру и электронной почты, поэтому мы реализовали только автоматические уведомления по SMS и Email, и интеграции с другими сервисами:

  • Клиентов об отгрузке продукции со складов в автомобили и вагоны (через интеграцию ERP с промышленными весами, подробнее во второй части).

  • Клиентов об убытие машины с территории завода со ссылкой-треком для отслеживания на карте (через интеграцию ERP с Wialon).

  • Клиентов о приближение машины у пункту разгрузки (через интеграцию ERP с Wialon).

  • Клиентов о прибытие вагонов на ЖД станцию для выгрузки (через интеграцию ERP с ЭТРАН).

  • Клиентов о необходимости разместить заказ (по расписанию для контактного лица).

  • Менеджеров по продажам о новых заявках клиентов, поступивших в нерабочее время.

  • Клиентов на самовывозе и перевозчиков - об опоздании водителей на погрузку (через интеграцию ERP с чат-ботом и уличным монитором, подробнее во второй и третьей части).

  • Перевозчиков о срочных заявках и опоздании водителей на погрузку (через интеграцию ERP с личным кабинетом перевозчика, подробнее в третьей части).

  • Перевозчиков о штрафах по опозданиям на погрузку и выгрузку (через интеграцию ERP с Wialon).

Как выглядят сообщения SMS-робота в ERP
СМС-робот в ERP для уведомленийСМС-робот в ERP для уведомлений

Все SMS отправляются с символьного имени, от лица компании.

Как выглядят письма Email-робота в ERP

Письма с вложениями и без:

  • Вложения - это отчеты в формате Excel, автоматические сформированные в ERP.

  • Формы отчетов Excel сразу адаптированы для печати на принтере.

  • Тексты писем адаптированы для чтения в смартфоне и ПК.

  • Письма в ERP синхронизованы с почтовым сервером.

Почтовый робот в ERP для уведомленийПочтовый робот в ERP для уведомлений

Интеграция ERP с Wialon: автоматический сбор данных по GPS

  1. Изначально, стояла задача реализовать автоматическое уведомление клиентов о статусе доставки продукции в режиме онлайн.

  2. Затем мы расширили интеграцию с Wialon и начали использовать сервис для автоматического фиксирования фактического времени прибытия машин на выгрузку.

Это набор готовых сервисов, связанный в единую подсистему. Срок реализации 2 месяца.

Пример SMS-сообщений показан на скриншоте выше.

Схема интеграции сервисов и данных для SMS-уведомлений клиентов о статусе доставки продукцииСхема интеграции сервисов и данных для SMS-уведомлений клиентов о статусе доставки продукции

Забегая вперед, мы также используем уведомления в чат-бот Telegram (подробнее в третье части). SMS - потому что не у всех контактных лиц клиентов есть доступ в интернет на адресах доставки, а мобильная сеть какая-никакая есть практически всегда.

Пример автоматического отчета для контроля прибытия машин на погрузку и выгрузку

На скриншоте отчет, а в ERP это целая подсистема с автоматической рассылкой уведомлений переводчикам о штрафах по опозданиям машин сверх допустимого договором лимита (подробнее во второй и третьей части).

Такая система, в связке с личным кабинетов перевозчика и чат-ботом для водителей (подробнее в третьей части):

  • Дисциплинирует перевозчиков и водителей соблюдать сроки погрузки и доставки

  • Повышает лояльность клиентов и гарантирует своевременные поставки точно в срок

Спасибо, что дочитали до конца!

Задавайте вопросы, постараюсь на все ответить.

Подробнее..

Экстракция данных из SAP HCM в non-SAP хранилища данных

23.09.2020 16:18:12 | Автор: admin
Как известно, компания SAP предлагает полный спектр программного обеспечения, как для ведения транзакционных данных, так и для обработки этих данных в системах анализа и отчетности. В частности платформа SAP Business Warehouse (SAP BW) представляет собой инструментарий для хранения и анализа данных, обладающий широкими техническими возможностями. При всех своих объективных преимуществах система SAP BW обладает одним значительным недостатком. Это высокая стоимость хранения и обработки данных, особенно заметная при использовании облачной SAP BW on Hana.

А что если в качестве хранилища начать использовать какой-нибудь non-SAP и желательно OpenSource продукт? Мы в Х5 Retail Group остановили свой выбор на GreenPlum. Это конечно решает вопрос стоимости, но при этом сразу появляются вопросы, которые при использовании SAP BW решались практически по умолчанию.



В частности, каким образом забирать данные из систем источников, которые в большинстве своем являются решениями SAP?

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

Экстракция данных


В SAP BW для SAP-систем существуют стандартные экстракторы данных. Эти экстракторы могут автоматически собирать необходимые данные, отслеживать их целостность, определять дельты изменений. Вот, например, стандартный источник данных по атрибутам сотрудника 0EMPLOYEE_ATTR:



Результат экстракции данных из него по одному сотруднику:



При необходимости такой экстрактор может быть модифицирован под собственные требования или может быть создан свой собственный экстрактор.

Первой возникла идея о возможности их переиспользования. К сожалению, это оказалось неосуществимой задачей. Большая часть логики реализована на стороне SAP BW, и безболезненно отделить экстрактор на источнике от SAP BW не получилось.

Стало очевидно, что потребуется разработка собственного механизма извлечения данных из SAP систем.

Структура хранения данных в SAP HCM


Для понимания требований к такому механизму, первоначально нужно определить какие именно данные нам потребуются.

Большинство данных в SAP HCM хранится в плоских SQL таблицах. На основании этих данных приложения SAP визуализируют пользователю оргструктуры, сотрудников и прочую HR информацию. Например, вот так в SAP HCM выглядит оргструктура:



Физически такое дерево хранится в двух таблицах в hrp1000 объекты и в hrp1001 связи между этими объектами.

Объекты Департамент 1 и Управление 1:



Связь между объектами:



Как типов объектов, так и типов связи между ними может быть огромное количество. Существуют как стандартные связи между объектами, так и кастомизированные для собственных специфичных нужд. Например, стандартная связь B012 между оргединицей и штатной должностью указывает на руководителя подразделения.

Отображение руководителя в SAP:



Хранение в таблице БД:



Данные по сотрудникам хранятся в таблицах pa*. Например, данные о кадровых мероприятиях по сотруднику хранятся в таблице pa0000



Мы приняли решение, что GreenPlum будет забирать сырые данные, т.е. просто копировать их из SAP таблиц. И уже непосредственно в GreenPlum они будут обрабатываться и преобразовываться в физические объекты (например, Отдел или Сотрудник) и метрики (например, среднесписочная численность).

Было определено порядка 70 таблиц, данные из которых необходимо передавать в GreenPlum. После чего мы приступили к проработке способа передачи этих данных.

SAP предлагает достаточно большое количество механизмов интеграции. Но самый простой способ прямой доступ к базе данных запрещен из-за лицензионных ограничений. Таким образом, все интеграционные потоки должны быть реализованы на уровне сервера приложений.
Следующей проблемой было отсутствие данных об удаленных записях в БД SAP. При удалении строки в БД, она удаляется физически. Т.е. формирование дельты изменений по времени изменения не представлялось возможным.

Конечно, в SAP HCM есть механизмы фиксация изменений данных. Например, для последующей передачи в системы получатели существуют указатели изменений(change pointer), которые фиксируют любые изменения и на основании которых формируются Idoc (объект для передачи во внешние системы).

Пример IDoc изменения инфотипа 0302 у сотрудника с табельным номером 1251445:



Или ведение логов изменений данных в таблице DBTABLOG.

Пример лога удаления записи с ключом QK53216375 из таблицы hrp1000:



Но эти механизмы доступны не для всех необходимых данных и их обработка на уровне сервера приложений может потреблять достаточно много ресурсов. Поэтому массовое включение логирования на все необходимые таблицы может привести к заметной деградации производительности системы.

Следующей серьезной проблемой были кластерные таблицы. Данные оценки времени и расчета зарплаты в RDBMS версии SAP HCM хранятся в виде набора логических таблиц по каждому сотруднику за каждый расчет. Эти логические таблицы в виде двоичных данных хранятся в таблице pcl2.

Кластер расчета заработной платы:



Данные из кластерных таблиц невозможно считать SQL командой, а требуется использование макрокоманд SAP HCM или специальных функциональных модулей. Соответственно, скорость считывания таких таблиц будет достаточно низка. С другой стороны, в таких кластерах хранятся данные, которые необходимы только раз в месяц итоговый расчет заработной платы и оценка времени. Так что скорость в этом случае не столь критична.

Оценивая варианты с формированием дельты изменения данных, решили так же рассмотреть вариант с полной выгрузкой. Вариант каждый день передавать гигабайты неизменных данных между системами не может выглядеть красиво. Однако он имеет и ряд преимуществ нет необходимости как реализации дельты на стороне источника, так и реализация встраивания этой дельты на стороне приемника. Соответственно, уменьшается стоимость и сроки реализации, и повышается надежность интеграции. При этом было определено, что практически все изменения в SAP HR происходят в горизонте трех месяцев до текущей даты. Таким образом, было принято решение остановиться на ежедневной полной выгрузке данных из SAP HR за N месяцев до текущей даты и на ежемесячной полной выгрузке. Параметр N зависит от конкретной таблицы
и колеблется от 1 до 15.

Для экстракции данных была предложена следующая схема:



Внешняя система формирует запрос и отправляет его в SAP HCM, там этот запрос проверяется на полноту данных и полномочия на доступ к таблицам. В случае успешной проверки, в SAP HCM отрабатывает программа, собирающая необходимые данные и передающая их в интеграционное решение Fuse. Fuse определяет необходимый топик в Kafka и передает данные туда. Далее данные из Kafka передаются в Stage Area GP.

Нас в данной цепочке интересует вопрос извлечения данных из SAP HCM. Остановимся на нем подробнее.

Схема взаимодействия SAP HCM-FUSE.



Внешняя система определяет время последнего успешного запроса в SAP.
Процесс может быть запущен по таймеру или иному событию, в том числе может быть установлен таймаут ожидания ответа с данными от SAP и инициация повторного запроса. После чего формирует запрос дельты и отправляет его в SAP.

Данные запроса передаются в body в формате json.
Метод http: POST.
Пример запроса:



Сервис SAP выполняет контроль запроса на полноту, соответствие текущей структуре SAP, наличие разрешения доступа к запрошенной таблице.

В случае ошибок сервис возвращает ответ с соответствующим кодом и описанием. В случае успешного контроля он создает фоновый процесс для формирования выборки, генерирует и синхронно возвращает уникальный id сессии.

Внешняя система в случае ошибки регистрирует ее в журнале. В случае успешного ответа передает id сессии и имя таблицы по которой был сделан запрос.

Внешняя система регистрирует текущую сессию как открытую. Если есть другие сессии по данной таблице, они закрываются с регистрацией предупреждения в журнале.

Фоновое задание SAP формирует курсор по заданным параметрам и пакет данных заданного размера. Размер пакета максимальное количество записей, которые процесс читает из БД. По умолчанию принимается равным 2000. Если в выборке БД больше записей, чем используемый размер пакета после передачи первого пакета формируется следующий блок с соответствующим offset и инкрементированным номером пакета. Номера инкрементируются на 1 и отправляются строго последовательно.

Далее SAP передает пакет на вход web-сервису внешней системы. А она система выполняет контроли входящего пакета. В системе должна быть зарегистрирована сессия с полученным id и она должна находиться в открытом статусе. Если номер пакета > 1 в системе должно быть зарегистрировано успешное получение предыдущего пакета (package_id-1).

В случае успешного контроля внешняя система парсит и сохраняет данные таблицы.

Дополнительно, если в пакете присутствует флаг final и сериализация прошла успешно, происходит уведомление модуля интеграции об успешном завершении обработки сессии и модуль обновляет статус сессии.

В случае ошибки контролей/разбора ошибка логируется и пакеты по данной сессии будут отклоняться внешней системой.

Так же и в обратном случае, когда внешняя система возвращает ошибку, она логируется и прекращается передача пакетов.

Для запроса данных на стороне SAP HСM был реализован интеграционный сервис. Сервис реализован на фреймворке ICF (SAP Internet Communication Framework help.sap.com/viewer/6da7259a6c4b1014b7d5e759cc76fd22/7.01.22/en-US/488d6e0ea6ed72d5e10000000a42189c.html). Он позволяет производить запрос данных из системы SAP HCM по определенным таблицам. При формировании запроса данных есть возможность задавать перечень конкретных полей и параметры фильтрации с целью получения необходимых данных. При этом реализация сервиса не предполагает какой-либо бизнес-логики. Алгоритмы расчёта дельты, параметров запроса, контроля целостности, и пр. также реализуются на стороне внешней системы.

Данный механизм позволяет собирать и передавать все необходимые данные за несколько часов. Такая скорость находится на грани приемлемой, поэтому это решение рассматривается нами как временное, позволившее закрыть потребность в инструменте экстракции на проекте.
В целевой картине для решения задачи экстракции данных прорабатываются варианты использования CDC систем типа Oracle Golden Gate или ETL инструментов типа SAP DS.
Подробнее..

Масштабный проект по внедрению SAP S4HANA в удаленном режиме Гибридный интеграционный ландшафт

08.06.2021 10:08:20 | Автор: admin

В предыдущей нашей статье мы рассказывали о том, какие уроки мы усвоили, как мы обучали коллег удаленно и как проводили тестирование системы. В данной статье речь пойдет об интеграционных ландшафтах. Для реализации решения в рамках нашего проекта мы выбрали гибридный интеграционный ландшафт на базе SAP PO и SAP MII. В данной статье мы рассмотрим особенности систем SAP PO и SAP MII, их предназначение, достоинства и недостатки. А также расскажем о трудностях, с которыми мы столкнулись при реализации проекта в 2020 году.

Итак, по порядку.

Интеграционные платформы: как выбрать?

Все компании рано или поздно приходят к решению интегрировать системы, которые они внедрили для решения различных операционных задач. И тут возникает вопрос, какую интеграционную систему выбрать.

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

Согласно SAP CIO Guide: Process and Data Integration in Hybrid Landscapes, существуют следующие классы интеграционных систем:

Process Integration охватывает интеграцию распределённого между системами/приложениями бизнес-процесса. Наиболее предпочтительные инструменты интеграции на базе ESB (Enterprise Service Bus)

Data Integration охватывает синхронизацию данных между приложениями, базами данных и озером данных вне транзакционного контекста. Наиболее предпочтительные инструменты интеграции на базе ETL (Extract, Transform, Load).

User Integration охватывает все задачи интеграции между сервером приложений и интерфейсами взаимодействия с пользователем.

Thing Integration охватывает все сценарии, когда данные из устройств, оборудования должны быть интегрированы с бизнес-приложениями.

Cross Use Cases остальные сценарии, применяемые для сквозного взаимодействия.

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

Опыт российской практики показывает, что компании в основном делают выбор в пользу таких интеграционных систем, как SAP PO (ранее SAP PI). Однако существуют и другие системы, которые также предназначены для интеграции между системами различных уровней. В нашем случае мы использовали интеграционные системы SAP PO и SAP MII.

Ключевые аспекты использования SAP PO и SAP MII

Когда мы подошли к выбору интеграционной платформы, то ориентировались на следующие наиболее важные аспекты, которые нужно учитывать при разработке архитектуры межсистемной интеграции:

1. SAP PO следует использовать для интеграции процессов в качестве основной Корпоративной сервисной шины (ESB) предприятия (Например, ERP с Банк-клиентом), а также когда требуется:

Использовать репозиторий корпоративных сервисов в качестве центрального репозитория SOA

Использовать поддержку дополнительных стандартов WS (web service), таких как UDDI, WS-BPEL, и задач, WS-RM в корпоративной среде.

Включение критически важных сценариев интеграции между бизнес-приложениями.

Использование новых функций, таких как аутентификация конечных пользователей, валидация XML и возможности BAM

Взаимодействие с системами, находящимися за рамками DMZ корпоративной сети.

2. SAP MII следует использовать в основном, когда стоит задача обрабатывать данные уровня заводов (MES, LIMS), которые представлены в разнородных хранилищах, которые требуется организовать и проанализировать, а также, когда нужно:

Получать согласованные отчеты о производственных операциях на разных сайтах.

Углубиться в хранилища данных предприятия и в разные хранилища данных для производственных интеллектуальных приложений.

Подключить собственные производственные приложения к данным и рабочим процессам SAP S/4HANA - через стандартные и встроенные адаптеры и инструменты (BAPI/RFC синхронный запрос).

Позволить персоналу предприятия выполнять отчетность (стандартную, мобильную и специальную) наряду с базовыми задачами.

Следует отметить, что система SAP MII по своему прямому назначению, помимо выполнения функции интеграционной шины, также дает возможность выполнять оперативные функции с использованием UI/UX-приложений и реализовать процессы по регистрации данных оперативного учета. Однако использование системы SAP MII только как платформы интеграции не дает никаких преимуществ по сравнению другими системами (например, SAP PO).

3. Наряду с отдельным использованием платформ SAP PO или SAP MII (рассматриваемых в данной статье) также нередки случаи совместного использования одновременно двух этих систем, а именно в тех ситуациях, когда:

SAP MII должен взаимодействовать c SAP S/4HANA (и другими системами корпоративного уровня) через SAP PO в интеграционных сценариях, требующих гарантированную доставку.

SAP PO должен использоваться для централизованного управления потоками данных.

SAP MII должен обеспечивать обработку данных уровня завода, даже в случае временной недоступности систем корпоративного уровня.

В SAP MII должны быть реализованы правила и обработки, специфичные для уровня завода.

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

Преимущества и недостатки SAP PI

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

Мы подготовили таблицу с описанием основных компонентов функционала и преимуществ, которые есть у каждой из систем по этим компонентам. Оценка функционала, представленная в данной таблице, является субъективным мнением консультантов и разработчиков и составлена на основе проектного опыта внедрения систем.

Среди недостатков функционала обеих систем отмечены следующие:

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

Вызовы нашего проекта, и как мы с ними справились

Реализация проектов интеграции систем - непростая задача, требующая согласованной работы различных команд в рамках одной локальной компании или, как в нашем случае, различных команд из нескольких стран с разными часовыми поясами и даже разными языками. К тому же дополнительный уровень сложности для проекта в 2020 году обеспечили пандемия и удаленный формат работы. Все эти трудности заставили нас по-новому посмотреть на взаимодействие между командами, а также внутри команды внедрения.

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

В связи с этим мы пересмотрели весь рабочий процесс, а именно:

1. Определили временные периоды, в рамках которых сотрудники могли начать и закончить свой рабочий день.

2. Перешли на scrum-методологию:

Ежедневные встречи, где в формате 15-минутных встреч обсуждали, что сделано, что будет сделано в течение дня и что мешает выполнению задач. Данные встречи помогают решать проблемы быстрее, чем бесконечные переписки.

Проведение встреч в формате видеоконференций. Такое взаимодействие, когда можно видеть друг друга, всегда положительно сказывается на производительности труда.

Общение посредством мессенджеров. Это также позволяет быстрее взаимодействовать с коллегами и предполагает менее формальный тон общения, что сближает сотрудников.

3. Хорошей практикой стала демонстрация Клиенту прототипа интеграционного решения. Мы показываем не только интеграцию, но и реализацию бизнес-процессов, и таким образом исключаем неприятные неожиданности.

4. Мы сделали оптимизацию с учетом разных часовых поясов:

Приоритизировали и распределяли задачи в соответствии с часовым поясом членов команд

Планировали ключевые встречи с учетом разницы во времени

С пониманием относились к экстренным запросам во внерабочее время.

5. Также одним их решений стал запрос и получение доступа в интегрируемые системы для того, чтобы процессы изучения систем, разработки и тестирования не останавливались вне зависимости от времени. Данное решение уменьшило зависимость от коллег при запуске тестового потока или просмотре полей.

Пересмотренный подход позволил нам скоординировать работу команд и её в срок.

Выводы

Существует большой выбор интеграционных платформ. Чтобы не утонуть в выборе, необходимо четко сформулировать задачи, которые стоят перед компанией, и далее определить класс необходимой системы. В случае если для решения ваших задач требуется гибридный интеграционный ландшафт, необходимо понять, какие задачи будут закрыты какой системой, оценить преимущества и недостатки каждой из выбранных систем и понять, как с ними работать в контексте вашей компании.

В нашем случае при принятии решения об использовании интеграционных систем мы полагались на опыт команды внедрения и опыт наших глобальных коллег, внедрявших системы SAP PO и SAP MII на проектах ранее. Это позволило нам учесть особенности обеих систем и построить эффективную архитектуру.

На собственном опыте мы знаем, что выбор всегда не прост, но возможен. И все проектные вызовы можно преодолеть и обратить в свои преимущества.

Надеемся, наша статья поможет вам в поиске наилучшего для вас решения. Да пребудет с Вами Сила!

Подробнее..

Разбираемся с FreePBX и интегрируем его с Битрикс24 и не только

09.09.2020 16:16:51 | Автор: admin

Битрикс24 - это огромный комбайн, который совмещает и CRM, и документооборот, и учет и еще много разных вещей, которые очень нравятся менеджерам и не очень нравятся IT персоналу. Портал используют очень много небольших и средних компаний, в том числе небольшие клиники, производственники и даже салоны красоты. Основной функцией, которую "любят" менеджеры является интеграция телефонии и CRM, когда любой звонок сразу фиксируется в CRM, создаются карточки клиента, при входящем отображается информация о клиенте и сразу видно кто он такой, что ему можно продать и сколько он должен. Но телефония от Битрикс24 и ее интеграция с CRM стоит денег, иногда немалых. В статье я расскажу опыт интеграции с открытыми инструментами и популярной IP АТС FreePBX, а также рассмотрю логику работы различных частей

Я работаю на аутсорсе в компании, которая занимается продажей и настройкой, интеграцией IP телефонии. Когда меня спросили, можем ли мы вон той вот и вот этой вот компании предложить что то для интеграции Битрикс24 с АТС, которые стоят у клиентов, а также с виртуальными АТС на различных VDS компании, я пошел в Гугл. И он мне конечно же выдал ссылку на статью в хабр, где есть и описание, и github, и вроде все работает. Но при попытке попользоваться этим решением вылезло, что Битрикс24 уже не тот, что ранее, и надо многое переделывать. Кроме того, FreePBX это вам не голый астериск, тут думать надо как совместить удобство использования и хардкорный диалплан в конфиг-файлах.

Изучаем логику работы

Итак для начала, как все это должно работать. При поступления звонка извне на АТС (событие SIP INVITE от провайдера) начинается обработка диалплана(плана набора, dialplan) - правил, что и в каком порядке делать со звонком. Из первого пакета можно получить много информации, которую потом можно использовать в правилах. Отличным инструментом для изучения внутренностей SIP является анализатор sngrep (ссылка) который просто ставится в популярных дистрибутивах через apt install/yum install и подобное, но можно и из исходников собрать. Посмотрим лог звонка в sngrep

В упрощенном виде диалплан занимается только первым пакетом, иногда также в процессе разговора совершается перевод звонков, нажатия кнопок (DTMF), разные интересности типа FollowMe, RingGroup, IVR и прочего.

Что внутри Invite пакета

Собственно большинство простых диалпланов работают с первыми двумя полями и вся логика крутится вокруг DID и CallerID. DID - куда звоним, CallerID - кто звонит.

Но ведь у нас фирма а не один телефон - и значит в АТС скорее всего есть группы вызова (одновременный/последовательный звонок нескольких аппаратов) на городских номерах (Ring Group), IVR (Здравствуйте, вы позвонили... Нажмите один для...), Автоответчики (Phrases), Временные условия (Time Conditions), Переадресация на другие номера или на сотовый (FollowMe, Forward). Это значит, что однозначно определить кому на самом деле придет вызов и с кем будет разговор при поступлении вызова очень сложно. Вот пример начала прохождения типового вызова в АТС наших клиентов

После успешного входа звонка в АТС происходит путешествие его по диалплану в разных "контекстах". Контекст с точки зрения Asterisk - это нумерованный набор команд, каждая из которых содержит фильтр по набранному номеру (он называется exten, для наружного вызова на начальном этапе exten=DID). Командами в строке диалплана может быть все что угодно - внутренние функции (например позвонить внутреннему абоненту - Dial(), положить трубку - Hangup()), условные операторы (IF, ELSE, ExecIF и подобные), переходы к другим правилам этого контекста (Goto, GotoIF), переход другим контекстам в виде вызова функций (Gosub, Macro). Отдельно стоит директива include имя_контекста, которая добавляет команды другого контекста в конец текущего контекста. Команды, включенные через include всегда выполняются после команд текущего контекста.

Вся логика работы FreePBX построена на включении друг в друга разных контекстов через include и вызов через Gosub, Macro и обработчики Handler. Рассмотрим контекст входящих вызовов FreePBX

Вызов проходит по всем контекстам сверху вниз по очереди, в кажом контексте могут быть вызовы других контекстов как макросов (Macro), функций(Gosub) или просто переходы (Goto), поэтому реальное дерево того, что вызывается можно отследить только в логах.

Типовая схема настройки типичной офисной АТС показана ниже. При вызове во входящих маршрутах ищется DID, по нему проверяются временные условия, если все в порядке - запускается голосовое меню. Из него по кнопке 1 или таймауту выход на группу дозвона операторов. После окончания звонка вызывается макрос hangupcall, после которого ничего уже в диалплане выполнить не удастся, кроме специальных обработчиков (hangup handler).

Где в этом алгоритме звонка мы должны поставлять информацию о начале звонка в CRM, где начинать запись, где оканчивать запись и отсылать ее вместе с информацией о звонке на CRM?

Интеграция с внешними системами

Что такое интеграция АТС и CRM? Это настройки и программы, которые конвертируют данные и события между двумя этими платформами и пересылают друг другу. Самым распространенным способом взаимодействия независимых систем является API, а самым популярным способом доступа к API является HTTP REST. Но только не для asterisk.

Внутри Asterisk есть:

  • AGI - синхронный вызов внешних программ/компонентов, используется в основном в диалплане, есть библиотеки типа phpagi, PAGI

  • AMI - текстовый TCP сокет, работающий по принципу подписки на события и ввода текстовых команд, напоминает SMTP изнутри, умеет отслеживать события и управлять вызовами, ,есть библиотека PAMI - самая популярная для создания связи с Asterisk

Пример вывода AMI

Event: NewchannelPrivilege: call,allChannel: PJSIP/VMS_pjsip-0000078bChannelState: 4ChannelStateDesc: RingCallerIDNum: 111222CallerIDName: 111222ConnectedLineNum: ConnectedLineName: Language: enAccountCode:Context: from-pstnExten: sPriority: 1Uniqueid: 1599589046.5244Linkedid: 1599589046.5244

  • ARI - смесь того, другого, все через REST, WebSocket, в формате JSON - но вот со свежими библиотеками и обертками не очень, на вскидку нашлись (phparia, phpari) которые становились в своем развитии года 3 назад.

Пример вывода ARI при инициации звонка

{ "variable":"CallMeCallerIDName", "value":"111222", "type":"ChannelVarset", "timestamp":"2020-09-09T09:38:36.269+0000", "channel":{ "id":"1599644315.5334", "name":"PJSIP/VMSpjsip-000007b6", "state":"Ring", "caller":{ "name":"111222", "number":"111222" }, "connected":{ "name":"", "number":"" }, "accountcode":"", "dialplan":{ "context":"from-pstn", "exten":"s", "priority":2, "appname":"Stasis", "appdata":"hello-world" }, "creationtime":"2020-09-09T09:38:35.926+0000", "language":"ru" }, "asteriskid":"48:5b:aa:aa:aa:aa", "application":"hello-world" }

Удобство или неудобство, возможность или невозможность работы с тем или иным API определяются задачами, которые необходимо решить. Задачи для интеграции с CRM следующие:

  • Отследить начало вызова, куда его перевели, вытащить CallerID, DID, времена начала и конца, может быть данные из директории (для поиска связи телефона и пользователя CRM)

  • Начать и окончить запись звонка, сохранить в нужном формате, сообщить по окончании записи где лежит файл

  • Инициировать звонок по внешнему событию (из программы), позвонить внутреннему номеру, внешнему и соединить их

  • Опционально: интегрировать с CRM, группами дозвона и FollowME для автоматического перевода звонков при отсутствии на месте(по информации CRM)

Все эти задачи можно решить через AMI или ARI, но ARI предоставляет гораздо меньше информации, нет многих событий, не отслеживаются многие переменные, которые в AMI все таки есть (например вызовы макросов, задание переменных внутри макросов, в том числе запись звонков). Поэтому, для правильного и точного отслеживания - выберем пока AMI (но не окончательно). Кроме того (ну а куда ж без этого, мы люди ленивые) - в исходной работе (статья в хабр) используют PAMI. *Потом надо попробовать переписать на ARI, но не факт что получится.

Придумываем интеграцию заново

Для того, чтоб наш FreePBX мог сообщать в AMI простыми способами о начале звонка, времени окончания, номерах, именах записанных файлов, рассчитывать длительность звонка проще всего воспользоваться тем же трюком, что и исходные авторы - ввести свои переменные и парсить вывод на их присутствие. PAMI предлагает это делать просто через функцию-фильтр.

Вот пример задания своей переменной для времени начала звонка (s - это специальный номер в диалплане, который выполняется ДО начала поиска по DID)

[ext-did-custom]exten => s,1,Set(CallStart=${STRFTIME(epoch,,%s)})
Пример AMI события по этой строке

Event: Newchannel

Privilege: call,all

Channel: PJSIP/VMS_pjsip-0000078b

ChannelState: 4

ChannelStateDesc: Ring

CallerIDNum: 111222

CallerIDName: 111222

ConnectedLineNum:

ConnectedLineName:

Language: en

AccountCode:

Context: from-pstn

Exten: s

Priority: 1

Uniqueid: 1599589046.5244

Linkedid: 1599589046.5244

Application: Set AppData:

CallStart=1599571046

Поскольку FreePBX перезаписывает файлы extention.conf и extention_additional.conf, мы будем использовать файл extention_custom.conf

Полный код extention_custom.conf
[globals];; Проверьте пути и права на папки - юзер asterisk должен иметь права на запись;; Сюда будет писаться разговорыWAV=/var/www/html/callme/records/wav MP3=/var/www/html/callme/records/mp3;; По этим путям будет воспроизводится и скачиваться записьURLRECORDS=https://www.host.ru/callmeplus/records/mp3;; Адрес для калбека при исходящем вызовеURLPHP=https://www.host.ru/callmeplus;; Да пишем разговорыRECORDING=1;; Это макрос для записи разговоров в нашу папку. ;; Можно использовать и системную запись, но пока пусть будет эта - ;; она работает[recording]exten => ~~s~~,1,Set(LOCAL(calling)=${ARG1})exten => ~~s~~,2,Set(LOCAL(called)=${ARG2})exten => ~~s~~,3,GotoIf($["${RECORDING}" = "1"]?4:14)exten => ~~s~~,4,Set(fname=${UNIQUEID}-${STRFTIME(${EPOCH},,%Y-%m-%d-%H_%M)}-${calling}-${called})exten => ~~s~~,5,Set(datedir=${STRFTIME(${EPOCH},,%Y/%m/%d)})exten => ~~s~~,6,System(mkdir -p ${MP3}/${datedir})exten => ~~s~~,7,System(mkdir -p ${WAV}/${datedir})exten => ~~s~~,8,Set(monopt=nice -n 19 /usr/bin/lame -b 32  --silent "${WAV}/${datedir}/${fname}.wav"  "${MP3}/${datedir}/${fname}.mp3" && rm -f "${WAV}/${fname}.wav" && chmod o+r "${MP3}/${datedir}/${fname}.mp3")exten => ~~s~~,9,Set(FullFname=${URLRECORDS}/${datedir}/${fname}.mp3)exten => ~~s~~,10,Set(CDR(filename)=${fname}.mp3)exten => ~~s~~,11,Set(CDR(recordingfile)=${fname}.wav)exten => ~~s~~,12,Set(CDR(realdst)=${called})exten => ~~s~~,13,MixMonitor(${WAV}/${datedir}/${fname}.wav,b,${monopt})exten => ~~s~~,14,NoOp(Finish if_recording_1)exten => ~~s~~,15,Return();; Это основной контекст для начала разговора[ext-did-custom];; Это хулиганство, делать это так и здесь, но работает - добавляем к номеру '8'exten =>  s,1,Set(CALLERID(num)=8${CALLERID(num)});; Тут всякие переменные для скриптаexten =>  s,n,Gosub(recording,~~s~~,1(${CALLERID(number)},${EXTEN}))exten =>  s,n,ExecIF(${CallMeCallerIDName}?Set(CALLERID(name)=${CallMeCallerIDName}):NoOp())exten =>  s,n,Set(CallStart=${STRFTIME(epoch,,%s)})exten =>  s,n,Set(CallMeDISPOSITION=${CDR(disposition)});; Самое главное! Обработчик окончания разговора. ;; Обычные пути обработки конца через (exten=>h,1,чтототут) в FreePBX не работают - Macro(hangupcall,) все портит. ;; Поэтому вешаем Hangup_Handler на окончание звонкаexten => s,n,Set(CHANNEL(hangup_handler_push)=sub-call-from-cid-ended,s,1(${CALLERID(num)},${EXTEN}));; Обработчик окончания входящего вызова[sub-call-from-cid-ended];; Сообщаем о значениях при конце звонкаexten => s,1,Set(CDR_PROP(disable)=true)exten => s,n,Set(CallStop=${STRFTIME(epoch,,%s)})exten => s,n,Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)});; Статус вызова - Ответ, не ответ...exten => s,n,Set(CallMeDISPOSITION=${CDR(disposition)})exten => s,n,Return;; Обработчик исходящих вызовов - все аналогичено[outbound-allroutes-custom];; Записьexten => _.,1,Gosub(recording,~~s~~,1(${CALLERID(number)},${EXTEN}));; Переменныеexten => _.,n,Set(__CallIntNum=${CALLERID(num)})exten => _.,n,Set(CallExtNum=${EXTEN})exten => _.,n,Set(CallStart=${STRFTIME(epoch,,%s)})exten => _.,n,Set(CallmeCALLID=${SIPCALLID});; Вешаем Hangup_Handler на окончание звонкаexten => _.,n,Set(CHANNEL(hangup_handler_push)=sub-call-internal-ended,s,1(${CALLERID(num)},${EXTEN}));; Обработчик окончания исходящего вызова[sub-call-internal-ended];; переменныеexten => s,1,Set(CDR_PROP(disable)=true)exten => s,n,Set(CallStop=${STRFTIME(epoch,,%s)})exten => s,n,Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)})exten => s,n,Set(CallMeDISPOSITION=${CDR(disposition)});; Вызов скрипта, который сообщит о звонке в CRM - это исходящий, ;; так что по факту окончанияexten => s,n,System(curl -s ${URLPHP}/CallMeOut.php --data action=sendcall2b24 --data ExtNum=${CallExtNum} --data call_id=${SIPCALLID} --data-urlencode FullFname='${FullFname}' --data CallIntNum=${CallIntNum} --data CallDuration=${CallMeDURATION} --data-urlencode CallDisposition='${CallMeDISPOSITION}')exten => s,n,Return

Особенность и отличием от оригинального диалплана авторов исходной статьи -

  • Диалплан в формате .conf, так как этого хочет FreePBX (да он умеет .ael, но не все версии и не всегда это удобно)

  • Вместо обработки окончания через exten=>h введена обработка через hangup_handler, потому что FreePBX диалплан заработал только с ним

  • Поправлена строка вызова скрипта, добавлены кавычки и внешний номер звонка ExtNum

  • Обработки вынесена в _custom контексты и позволяют не трогать и не править конфиги FreePBX - входящие через [ext-did-custom], исходящие через [outbound-allroutes-custom]

  • Нет привязки к номерам - файл универсален и нуждается в настройке только пути и ссылка на сервер

Для начала работы нужно еще пустить скрипты в AMI по логину и паролю - для этого в FreePBX тоже есть _custom файл

Файл manager_custom.conf
;;  это логин[callmeplus];; это парольsecret = trampampamturlaladeny = 0.0.0.0/0.0.0.0;; я работаю с локальной машиной - но если надо, можно и другие прописатьpermit = 127.0.0.1/255.255.255.255read = system,call,log,verbose,agent,user,config,dtmf,reporting,cdr,dialplanwrite = system,call,agent,log,verbose,user,config,command,reporting,originate

Эти оба файла надо поместить в /etc/asterisk, затем перечитать конфиги (или перезапустить астериск)

# astrisk -rv  Connected to Asterisk 16.6.2 currently running on freepbx (pid = 31629)#freepbx*CLI> dialplan reload     Dialplan reloaded.#freepbx*CLI> exit

Теперь перейдем к PHP

Инициализация скриптов и создание сервиса

Поскольку схема работы с Битрикс 24, сервисом для AMI не совсем проста и прозрачна, на ней надо остановится отдельно. Астериск при активации AMI просто открывает порт и все. При присоединении клиента она запрашивает авторизацию, потом клиент подписывается на нужные события. События приходят простым текстом, который PAMI преобразует в структурированные объекты и предоставляет возможность задание функции фильтрации только по интересующим событиям, полям, номерам и т.д.

Как только звонок поступает, возникает событие NewExten начиная с родительского контекста [from-pstn], затем идут все события по порядку следования строк в контекстах. При получении информации из заданных в диалплане _custom переменных CallMeCallerIDName и CallStart вызывается

  1. Функция запроса UserID, соответствующий внутреннему номеру, куда пришел звонок. А если это группа дозвона? Вопрос политический, надо создать звонок всем сразу (когда звонят все сразу) или создавать по мере обзвона при поочередном звонке? У большинства клиентов стоит стратегия Fisrt Available, поэтому с этим нет проблем, звонит только один. Но решать вопрос надо

  2. Функция регистрации звонка в Битрикс24, которая возвращает CallID, необходимый потом для сообщения о параметрах звонка и ссылке на запись. Требует или внутренний номер или UserID

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

Поскольку модуль CallMeIn.php должен работать непрерывно, для него был создан SystemD файл запуска callme.service, который надо положить в /etc/systemd/system/callme.service

[Unit]Description=CallMe[Service]WorkingDirectory=/var/www/html/callmeplusExecStart=/usr/bin/php /var/www/html/callmeplus/CallMeIn.php 2>&1 >>/var/log/callmeplus.logExecStop=/bin/kill -WINCH ${MAINPID}KillSignal=SIGKILLRestart=on-failureRestartSec=10s#тут надо смотреть,какие права на папки#User=www-data  #Ubuntu - debian#User=nginx #Centos[Install]WantedBy=multi-user.target

инициализация и запуск скрипта происходит через systemctl или service

# systemctl enable callme# systemctl start callme

Сервис будет сам перезапускаться по необходимости (при падениях). Сервис слежения за входящими не требует установки веб сервера, нужен только php (который точно есть на сервере FeePBX). Но при отсутствии доступа к записям звонков через Веб сервер (еще и с https) не будет возможности прослушивать записи разговоров.

Теперь поговорим про исходящие звонки. У скрипта CallMeOut.php две функции:

  • Инициация звонка при поступлении запроса на php скрипт (в том числе по кнопке "Позвонить" в самом битриксе). Без веб сервера не работает, запрос поступает через HTTP POST, в запросе содержится токен

  • Сообщение о звонке, его параметрах и записях в Битрикс. Происходит по инициативе Asterisk в диалплане [sub-call-internal-ended] при окончании звонка

Веб сервер нужен только для двух вещей - загрузка файлов записей битриксом (по HTTPS) и вызов скрипта CallMeOut.php. Можно использовать встроенный сервер FreePBX, файлы для которого лежат /var/www/html, можно установить другой сервер или прописать другой путь.

Веб сервер

Оставим настройку веб сервера на самостоятельное изучение (тыц, тыц, тыц). Если у вас нет домена, можно попробовать FreeDomain( https://www.freenom.com/ru/index.html), которые на халяву дадут вам имя для вашего белого IP (не забудьте пробросить порты 80, 443 через роутер, если внешний адрес есть только на нем). Если вы только создали DNS домен, то надо подождать (от 15 минут до 48 часов) пока все сервера прогрузятся. По опыту работы с отечественными порвайдерами - от 1 часа до суток.

Автоматизация установки

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

Docker

Если хочется быстро попробовать решение - есть вариант с Docker - быстро создать контейнер, дать ему порты наружу, подсунуть файлы настроек и попробовать (это вариант с LetsEncrypt контейнером, если сертификат уже есть, просто нужно перенаправить обратный прокси на веб сервер FreePBX (ему мы дали другой порт - 88), LetsEncrypt в докере по мотивам этой статьи

Запускать файл надо в скачанной папке проекта (после git clone), но предварительно залезть в конфиги астериска (папка asterisk) и прописать там пути к записям и URL вашего сайта

version: '3.3'services:  nginx:    image: nginx:1.15-alpine    ports:      - "80:80"      - "443:443"    volumes:      - ./nginx/ssl_docker.conf:/etc/nginx/conf.d/ssl_docker.conf  certbot:    image: certbot/certbot  freepbx:    image: flaviostutz/freepbx    ports:      - 88:80 # для настройки      - 5060:5060/udp      - 5160:5160/udp      - 127.0.0.1:5038:5038 # для CallMeOut.php#      - 3306:3306      - 18000-18100:18000-18100/udp    restart: always    environment:      - ADMIN_PASSWORD=admin123    volumes:      - backup:/backup      - recordings:/var/spool/asterisk/monitor      - ./callme:/var/www/html/callme      - ./systemd/callme.service:/etc/systemd/system/callme.conf      - ./asterisk/manager_custom.conf:/etc/asterisk/manager_custom.conf      - ./asterisk/extensions_custom.conf:/etc/asterisk/extensions_custom.conf#      - ./conf/startup.sh:/startup.shvolumes:  backup:  recordings:

Этот файл docker-compose.yaml, запускается через

docker-compose up -d

Если nginx не запустился, значит что то не так с конфигурацией в папке nginx/ssl_docker.conf

Другие интеграции

А почему бы заодно не сунуть несколько CRM в скрипты, подумали мы. Изучили несколько API других CRM, особенно бесплатной встроенной в некоторые АТС - ShugarCRM и Vtiger, и да! можно, принцип тот же. Но это уже другая история, которую потом будем заливать на гитхаб отдельно.

Ссылки

Дисклеймер: любые совпадения с реальность вымышлены и это был не я,

Подробнее..

C и Python мост между мирами

14.03.2021 12:14:15 | Автор: admin

Подход к снаряду

Не так давно, в феврале, у меня случился замечательный день: ещё один проект-долгострой "полетел". О чём речь? Речь о моей давней задумке на тему использования интерпретатора Python в программах на C: я реализовал добавление хуков на Python в ReOpenLDAP. Сама по себе тема, понятное дело, большая, поэтому в таких ситуациях я пишу минимальный код на C, который служит как раз для проверки концепта и его обкатки - его очень удобно запускать под инструментами типа Valgrind, которые незамедлительно укажут на явные ошибки ляпы в работе с памятью. Однако после окончания работы я понял, что сам по себе минимальный код может быть полезен кому-то ещё, кроме меня. Почему? Потому перед началом работы я наивно предполагал, что официальная документация по C API поможет всё сделать легко и быстро, но увы! - внятного примера с пошаговым разбором не нашёл. Что ж, это open source, детка, не нравится - сделай сам.

Для большей точности: пример разрабатывался на CentOS 7 с установленными пакетами python3, python3-devel, то есть всё описанное было написано, отлажено и проделано запущено именно в этом окружении.

Для удобства весь разбираемый код лежит в репозитории на моём ГитХабе.

Инициализация

Начало начал - подключение к нашей программе заголовочного файла:

#include <Python.h>

Далее готовим нужные константы (понятно, что у вас их значения будут другими, просто следите за смыслом значений):

char hook_file_path[] = "./";char hook_file[] = "ldap_hooks";char *hook_functions[] = {  "add_hook","bind_hook","unbind_hook","compare_hook","delete_hook","modify_hook",  "modrdn_hook","search_hook","abandon_hook","extended_hook","response_hook",NULL };PyObject *pName, *pModule, *pFunc, *pValue, *sys, *path, *newPaths;

Здесь:

  • hook_file_path - каталог в файловой системе, в котором вы хотите хранить свой код на Python;

  • hook_file- имя файла с кодом, расширение .py указывать не надо;

  • hook_functions - массив с названиями функций в файле, которые мы будем искать и вызывать; последний элемент, NULL, использован как костыль для обозначения конца массива.

Дальше объявляем несколько указателей на PyObject - пока что они нам не принципиальны, просто имейте в виду, что они есть.

Готовим интерпретатор к работе:

Py_Initialize();
HERE BE DRAGONS

Помните, что в программе фактически придётся управлять памятью сразу в двух местах: на уровне кучи (malloc/free), и на уровне чёрного ящика интерпретатора Питона. Объекты, возвращаемые функциями интерпретатора, будут размещёны в памяти, им же и управляемой, поэтому придётся периодически сообщать интерпретатору Python, что тот или иной объект мы больше не используем, и можно его добавить в список для garbage collector'а. Для этого нам пригодится вызов Py_XDECREF(*Py_Object).Он умеет сам проверять, не NULL ли передан в параметре, и если да - функция не делает ничего, в отличие от Py_DECREF(*Py_Object), которая в этом случае вернёт ошибку.

Далее загружаем модуль sys и добавляем в его список path нужный нам путь:

// credits to https://stackoverflow.com/questions/50198057/python-c-api-free-errors-after-using-py-setpath-and-py-getpath// get handle to python sys.path objectsys = PyImport_ImportModule("sys");path = PyObject_GetAttrString(sys, "path");// make a list of paths to add to sys.pathnewPaths = PyUnicode_Split(PyUnicode_FromString(hook_file_path), PyUnicode_FromWideChar(L":", 1), -1);// iterate through list and add all pathsfor(i=0; i<PyList_Size(newPaths); i++) {    PyList_Append(path, PyList_GetItem(newPaths, i));}Py_XDECREF(newPaths);Py_XDECREF(path);Py_XDECREF(sys);

Я не делаю какого-то большого секрета из того факта, что часть этого кода взята со StackOverflow: что же тут поделать, ищу специфические вещи, которые редко в полном объёме покрываются документацией.

Загрузка файла с Python-кодом

Дальше будет чуть попроще - всего лишь выполним importдля нашего модуля. Почему так - ровно потому, что налицо проблема курицы и яйца: некому вызвать import ldap_hooks.

pName = PyUnicode_DecodeFSDefault(hook_file);if (pName == NULL){  fprintf(stderr,"No Python hook file found\n");  return 1;}pModule = PyImport_Import(pName);Py_DECREF(pName);// fprintf(stderr,"No C errors until now\n");

Поиск функций в файле и их вызов

Итак, теперь у нас есть загруженный в память интерпретатор, готовый к работе, а его состояние соответствует тому, как если бы мы из кода на Python вызвали import для файла, чьё имя указано в строке hook_file.

Далее получаем объект нужной функции и вызываем её:

pFunc = PyObject_GetAttrString(pModule,hook_functions[i]);if (pFunc && PyCallable_Check(pFunc)) {    fprintf(stderr,"function %s exists and can be called\n", hook_functions[i]);    fprintf(stderr, "Calling %s\n", hook_functions[i]);    pValue = PyObject_CallFunction(pFunc, "s", hook_functions[i]);

Обратите внимание: после получения объекта по имени всегда полезно проверить, можем ли мы к нему обратиться. Именно это делает вторая строка. А пятая строка этого фрагмента вызывает функцию, передавая ей аргумент типа "строка" (на это указывает "s"). Для удобства каждая функция нашего кода на Python будет вызываться с единственным строковым аргументом, равным названию этой самой функции.

Вообще по документацииPyObject_CallFunction ровно так и вызывается:

  • первый параметр - объект вызываемой функции в Python-коде, ранее полученный через PyObject_GetAttrString;

  • второй, строка - сообщает интерпретатору тип и количество аргументов (более подробно об этой строке - в документации);

  • третий и далее аргументы - аргументы, то есть то, что наша Python-функция получит внутри входного кортежа (питонистам это известно как *args).

Итак, ссылка на объект, содержащий в себе то, что вернул наш код на Python - в pyValue. Можно праздновать?... Нет, рано. Переходим к следующей части.

Разбор результата

Всё многообразие возвращаемых результатов можно свести к базовым типам - их и будем разбирать. Очередной фрагмент кода из-за длины под спойлером.

Осторожно, код
if (pValue != NULL) {  if (pValue == Py_None) {   fprintf(stderr,"==> Дружище, это None, тут правда ничего нет\n");  }  else if ((pValue == Py_False) || (pValue == Py_True)) {    fprintf(stderr,"==> Bool:\n");    if (pValue == Py_False) {      fprintf(stderr, " False\n");    } else {      fprintf(stderr, " True \n");    }  } else if (PyUnicode_Check(pValue)) {    fprintf(stderr,"==> String:\n");    const char* newstr = PyUnicode_AsUTF8(pValue);    fprintf(stderr,"\"%s\"\n", newstr);  } else if (PyDict_Check(pValue)) {    PyObject *key, *value;    Py_ssize_t pos =0;    fprintf(stderr,"==> Dict:\n");    while (PyDict_Next(pValue, &pos, &key, &value)) {     fprintf(stderr, "%s: %s\n", PyUnicode_AsUTF8(key), PyUnicode_AsUTF8(value));    }  } else if (PyList_Check(pValue)) {    fprintf(stderr,"==> List:\n");    Py_ssize_t i, seq_len;    PyObject *item;    seq_len = PyList_Size(pValue);    for (i=0; i<seq_len; i++) {      item = PyList_GetItem(pValue, i);      fprintf(stderr, " %s\n", PyUnicode_AsUTF8(item));      // !!!--> NOT NEEDED <--!!!  Py_DECREF(item);      }  } else if (PyTuple_Check(pValue)) {    fprintf(stderr,"==> Tuple:\n");    Py_ssize_t i, seq_len;    PyObject *item;    seq_len = PyTuple_Size(pValue);    for (i=0; i<seq_len; i++) {      item = PyTuple_GetItem(pValue, i);      fprintf(stderr, " %s\n", PyUnicode_AsUTF8(item));      // !!!--> NOT NEEDED <--!!! Py_DECREF(item);      }  } else if (PyFloat_Check(pValue)) {    fprintf(stderr, "==> Float: %f\n", PyFloat_AsDouble(pValue));  } else if (PyLong_Check(pValue)) {    fprintf(stderr, "==> Long: %ld\n", PyLong_AsLong(pValue));  } else if (PySet_Check(pValue)) {    fprintf(stderr,"==> Set:\n");    PyObject *str_repr = PyObject_Repr(pValue);    fprintf(stderr, " %s\n", PyUnicode_AsUTF8(str_repr));    Py_XDECREF(str_repr);  } else {    fprintf(stderr, "==> Какая-то дичь! Проверь-ка тип результата функции %s\n", hook_functions[i]);  }  Py_XDECREF(pValue);} else {  fprintf(stderr, "WTF");}

Некоторые важные моменты по поводу разбора результатов, возвращаемых функциями:

  • особняком стоят значения None, True и False: для них нет каких-то отдельных проверочных функций, и мы в коде на C проверяем, не они ли это, простым сравнением со специальными константами: Py_None, Py_True, Py_False;

  • значения-словари для иллюстрации статьи обойдём встроенным итератором, но вообще, конечно, можем получить нужный элемент по ключу;

  • для списков и кортежей функции вида PyXXXX_GetItem возвращают "чужие" ссылки - то есть вместе с ними вашему коду на C не передаётся ни владение объектом, ни обязанность этот объект уничтожить через Py_DECREF()

  • если реализовать поддержку не конкретных типов, а протоколов - ваш C-код получит способность поддерживать питонячью утиную типизацию.

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

Функции обработки результатов

Значение в Python

Проверка

Использование в C

None

True

False

pValue == Py_None

pValue == PyTrue

pValue == Py_False

нет

строка

PyUnicode_Check(pValue)

PyUnicode_AsUTF8(pValue)

словарь

PyDict_Check(pValue)

PyObject *key, *value;
Py_ssize_t pos =0;
while ( PyDict_Next(
pValue, &pos, &key, &value)) {
.....
}

список

PyList_Check(pValue)

Py_ssize_t i, seq_len;
PyObject *item;
seq_len = PyList_Size(pValue);
for (i=0; i<seq_len; i++) {
item = PyList_GetItem(pValue, i);
.....}

кортеж

PyTuple_Check(pValue)

Py_ssize_t i, seq_len;
PyObject *item;
seq_len = PyTuple_Size(pValue);
for (i=0; i<seq_len; i++) {
item = PyTuple_GetItem(pValue, i);
.....}

число с плавающей точкой

PyFloat_Check(pValue)

PyFloat_AsDouble(pValue)

целое число

PyLong_Check(pValue)

PyLong_AsLong(pValue)

Заключение

В статье намеренно не освещались вопросы передачи каких-нибудь хитросложенных аргументов, объявления объектов-типов внутри интерпретатора и тому подобные вещи, включая обработку ошибок Python-кода - это всё-таки crash-course, а не олимпиада. Поэтому на этом откланиваюсь, и могу только добавить, что весь код лежит в репозитории на моём ГитХабе, а в комментариях попробую ответить на вопросы по теме статьи.

UPD. Поправил очепятку (Py_DECREF(*Py_Object) -> Py_DECREF(*Py_Object)).

Подробнее..

Как мы запустили документооборот в Telegram и что из этого вышло? Да, это не сон

24.05.2021 12:21:25 | Автор: admin

Разбираем аргументы за и против. В конце также можно ознакомиться с моим мнением на этот счет.

С чего все начиналось?

Решение сложной задачи часто оказывается простым и гениальным. Так и вышло, когда ко мне обратилась крупная компания с проблемой потери прибыли. И дело было не в утечке или неграмотном ведении бизнес-процессов, а элементарно в долгих паузах в документообороте.

Немного обо мне: я Python разработчик, архитектор, тимлид. В программировании с 2009 года. Ранее опубликовал эту статью на vc.ru.

В реализации проекта мне помогал аналитик от заказчика, и в общем-то всё.

Итак, к кейсу

В коллектив организации входит большое количество выездных сотрудников. Часто возникают ситуации, когда у работника нет возможности CRM-системой на компьютере (в нашем случае ноутбуке) для выполнения рабочих задач.

Если коротко описать задачу: человек в полевых условиях должен иметь возможность войти в систему и отправить необходимые документы. Иначе из-за временных задержек компания теряет приличные суммы денег.

Более подробно описать ситуации я, к сожалению, не могу, т.к. есть договоренность о неразглашении подробностей этого кейса.

Как решить проблему потери времени? У сотрудника должна быть возможность коннектиться с CRM-системой с планшета или телефона.

Как осуществить задуманное?

Выход нашёлся быстро: создать чат-бот в Telegram.

И на это решение можно посмотреть с двух сторон: с позиции управленцев и со стороны айтишников.

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

Если рассуждать со стороны управленцев, соединение CRM-системы с чат-ботом Telegram значительно снижает энергозатраты и финансовые расходы компании. И вот, почему:

  • Не нужно обучать большой штат новым программам-интеграторам CRM с телефоном.

  • Тем более, стоимость таких программ значительно выше, чем стоимость написания и поддержания работоспособности чат-бота для Telegram.

  • Это удобно. Сейчас у каждого есть возможность скачать Telegram, который не занимает много места в памяти телефона.

  • Работает Telegram-бот действительно быстрее, чем сложная программа. Кроме того, чат-бот помогает сотруднику сориентироваться во внутреннем документообороте фирмы.


Но со стороны безопасности и сохранности данных такое внедрение в электронный документооборот крупной компании нужно считать неприемлемым. Здесь на арену выходят возможные доводы IT-отдела.

  • Интеграция мессенджера в CRM-систему может привести к утечке информации. Сам по себе документооборот необходимо строить на основе внутренних ресурсов, также необходимо предусмотреть авторизацию пользователей. Если не учесть эти моменты, то данные могут уйти не в те руки.

  • Если изменить процесс документооборота, например, ввести отправку редактированных вариантов, то решение рассыпается. В таком случае сотруднику необходимо будет сначала отредактировать документ, а потом отправить его в чат с ботом.

С точки зрения офисного сотрудника это легко выполнимая задача. А со стороны полевого человека, это сильно усложняет процесс работы с документами.

  • Ещё один весомый аргумент: данные будут храниться в разных системах. Telegram-бот может быть напрямую привязан к CRM или ERP-системе, но в большинстве случаев он имеет свое хранилище, в котором агрегирует данные и обрабатывает их.

Если не обратить на это внимание, то может наступить такой момент, когда данные в системах будут отличаться.

А что я думаю по этому поводу?

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

Тем более, если говорить об удобстве главных героев этой эпопеи - полевых работниках, то загрузить документ и отправить его на дальнейшее согласование, получить нужное уведомление гораздо проще через бот в Telegram, чем через специализированную программу, типа 1C, Диадок и т.д.

По моему мнению, идея с созданием чат-бота в Telegram является довольно простой с точки зрения использования и реализации. Да, он имеет интеграции с другими системами, но мы решили проблему удобства и оперативности получения информации.

Даже удивительно, почему другие компании не используют подобные методы. А может и использует, кто их знает?

Главное учесть, что чат-бот подойдёт для решения не всех задач, но, когда нужно разобраться с простыми вопросами быстро и вовремя, Telegram-бот это то, что нужно.

О технической стороне вопроса

Идея была реализована на основе Telegram API на вебхуках. Для разработки был использован любимый python, данные хранятся на базе postgresql. Для ускорения работы и асинхронности задач применили связку redis + celery, в качестве серверной операционной системы использована Ubuntu 18 Server.

К слову сказать, мой клиент остался доволен. Нововведение принесло компании прибыль, которая терялась на простоях из-за пауз в цепочках бизнес-процессов.

А вы на стороне программистов или управленцев? Делитесь своим мнением, задавайте вопросы!

Подробнее..

Интеграция PHP проекта на GitHub и Scrutinizer

13.01.2021 04:12:47 | Автор: admin

Регистрируемся в Scrutinizer

Проще, наверное, войти через вашу учетную запись в GitHub, т.к. это избавит вас от необходимости линковать ваш GitHub профиль, а делать это все равно придется.

Для установки приложения Scrutinizer переходим по ссылке, этот шаг можно пропустить, но имейте ввиду, что в дальнейшем у вас не будут работать интерактивные плюшки и будет висеть нотификация: Scrutinizer GitHub App Is Not Installed...

Даем ему все скоупы, которые требует. Не бойтесь, Scrutinizer не удалит ваши репозитории и никак вам не навредит. Это нужно для того, чтобы приложение могло общаться с вашим GitHub по АПИ и в real-time управлять вашим Check Suite, давая ему обратную связь, и кодом в ваших PR для удобства ревью и тд.

Пример, как выглядит окно установки Scrutinizer

Создаем глобальную конфигурацию

Вообще говоря, если у вас много репозиториев, то Best Practies считается правильным иметь одну глобальную конфигурацию, где хранить общие настройки, а специфичные конфиги выносить в конфигурации ваших репозиториев, как бы перезаписывая отдельные куски конфигураций.

Чтобы создать глобальную конфигурацию перейдите по ссылке.

Пример глобальной конфигурации для PHP проекта
build:    environment:        php: 7.3.15build_failure_conditions:  - 'project.metric_change("scrutinizer.quality", < -0.10)'  - 'elements.rating(<= D).exists'                                # No classes/methods with a rating of D or worse  - 'elements.rating(<= D).new.exists'                            # No new classes/methods with a rating of D or worse allowed  - 'issues.label("coding-style").exists'                         # No coding style issues allowed  - 'issues.label("coding-style").new.exists'                     # No new coding style issues allowed  - 'issues.severity(>= MAJOR).new.exists'                        # New issues of major or higher severity                          - 'project.metric("scrutinizer.quality", < 9)'                  # Code Quality Rating drops below 9  - 'patches.label("Doc Comments").exists'                        # No doc comments patches allowed  - 'patches.label("Spacing").exists'                             # No spacing patches allowedchecks:    php:        verify_property_names: true        verify_argument_usable_as_reference: true        verify_access_scope_valid: true        variable_existence: true        useless_calls: true        use_statement_alias_conflict: true        unused_variables: true        unused_properties: true        unused_parameters: true        unused_methods: true        unreachable_code: true        too_many_arguments: true        symfony_request_injection: true        switch_fallthrough_commented: true        sql_injection_vulnerabilities: true        simplify_boolean_return: true        security_vulnerabilities: true        return_in_constructor: true        return_doc_comments: true        return_doc_comment_if_not_inferrable: true        require_scope_for_methods: true        require_php_tag_first: true        remove_extra_empty_lines: true        property_assignments: true        properties_in_camelcaps: true        precedence_mistakes: true        precedence_in_conditions: true        phpunit_assertions: true        parse_doc_comments: true        parameters_in_camelcaps: true        parameter_non_unique: true        parameter_doc_comments: true        param_doc_comment_if_not_inferrable: true        overriding_private_members: true        overriding_parameter: true        non_commented_empty_catch_block: true        no_trait_type_hints: true        no_trailing_whitespace: true        no_short_variable_names:            minimum: '3'        no_short_open_tag: true        no_short_method_names:            minimum: '3'        no_property_on_interface: true        no_non_implemented_abstract_methods: true        no_long_variable_names:            maximum: '20'        no_goto: true        no_exit: true        no_eval: true        no_error_suppression: true        no_debug_code: true        naming_conventions:            local_variable: '^[a-z][a-zA-Z0-9]*$'            abstract_class_name: ^Abstract|Factory$            utility_class_name: 'Utils?$'            constant_name: '^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$'            property_name: '^[a-z][a-zA-Z0-9]*$'            method_name: '^(?:[a-z]|__)[a-zA-Z0-9]*$'            parameter_name: '^[a-z][a-zA-Z0-9]*$'            interface_name: '^[A-Z][a-zA-Z0-9]*Interface$'            type_name: '^[A-Z][a-zA-Z0-9]*$'            exception_name: '^[A-Z][a-zA-Z0-9]*Exception$'            isser_method_name: '^(?:is|has|should|may|supports)'        more_specific_types_in_doc_comments: true        missing_arguments: true        method_calls_on_non_object: true        instanceof_class_exists: true        foreach_usable_as_reference: true        foreach_traversable: true        fix_use_statements:            remove_unused: true            preserve_multiple: false            preserve_blanklines: false            order_alphabetically: false        fix_line_ending: true        fix_doc_comments: true        encourage_shallow_comparison: true        duplication: true        deprecated_code_usage: true        deadlock_detection_in_loops: true        comparison_always_same_result: true        code_rating: true        closure_use_not_conflicting: true        closure_use_modifiable: true        check_method_contracts:            verify_interface_like_constraints: true            verify_documented_constraints: true            verify_parent_constraints: true        catch_class_exists: true        call_to_parent_method: true        avoid_superglobals: true        avoid_length_functions_in_loops: true        avoid_entity_manager_injection: true        avoid_duplicate_types: true        avoid_closing_tag: true        assignment_of_null_return: true        argument_type_checks: true

Обратите внимание, что проверки, описанные ниже, будут приводить к фейлу сборки, поэтому, если для вас некоторые из них не нужны, удалите или понизьте лимиты:

Проверки для сборок
build_failure_conditions:  - 'project.metric_change("scrutinizer.quality", < -0.10)'  - 'elements.rating(<= D).exists'  - 'elements.rating(<= D).new.exists'   - 'issues.label("coding-style").exists'   - 'issues.label("coding-style").new.exists'                - 'issues.severity(>= MAJOR).new.exists'                                       - 'project.metric("scrutinizer.quality", < 9)'  - 'patches.label("Doc Comments").exists'  - 'patches.label("Spacing").exists' 

Хотел бы отметить, всегда указывайте ваше окружение (версию PHP, базы данных и тд), т.к. значения по умолчанию могут меняться и ваши сборки в один прекрасный день будут зафейлены. Что собственно и приключилось со мной буквально на днях, когда релизнулся PHP 8.0.1.

Настройка репозитория

По сути мы просто добавляем свой репозиторий с GitHub, для этого переходим по ссылке.

Пример окна добавления репозитория

Начинаете вводить название репозитория и автозаполнение автоматически выдаст все доступные публичные репозитории, указываете язык и все.

Далее идем в настройки вашего репозитория, которые находятся вот тут:
https://scrutinizer-ci.com/g/ваш-логин/название-репозитория/settings

Настройка Check Suite

Тут немного остановимся и я расскажу почему.

Дело в том, что сборки Scrutinizer довольно медленные, даже для одного окружения сборка с анализом и покрытием может длиться минут 20.

За это время ваши обычные проверки в GitHub CI уже могут быть пройдены, линтеры показали ваши ошибки или вы вспомнили, что что-то забыли, и успеели толкнуть пару-тройку коммитов до завершения сборки Scrutinizer.

Tracking Settings

По умолчанию каждый коммит будет добавлять в очередь сборку. Таким образом, чтобы дождаться заветной зеленой галочки в CheckSuite, придется ждать не один час, ну а если там ошибка или ваши сборки очень сложные, то и того дольше.

  • Pull-Request Tracking - отвечает за запуск сборок только для Pull-реквестов в основную ветку.

  • Pull-Request Notification - отвечает за то, как вы хотите узнавать о результатах сборки и анализа, например, через GitHub Check Suite.

  • Tracked Branches - отвечает за запуск сборок при пушах. Рекомендую, выставить опцию "Track only branches listed below" и указать вашу основную ветку.

Здесь находится пример с правильными настройками

Auto-Cancel Non-Finished Inspections

Если вы все же успели толкнуть пару коммитов до завершения сборок, эти настройки отменят предыдущие задания и сразу перезапустят их для вашего последнего коммита.

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

Здесь находится пример с правильными настройками

Настройка конфигурации репозитория

Переходим по ссылке:
https://scrutinizer-ci.com/g/ваш-логин/название-репозитория/settings/build-config

Важно выбрать вашу глобальную конфигурацию.

Пример, как задействовать глобальную конфигурацию
Пример конфигурации для проекта для Postgres
build:    nodes:        coverage:            services:                postgres: 12            tests:                override:                    -                        command: |                            sed -e "s/\${USERNAME}/scrutinizer/" \                                -e "s/\${PASSWORD}/scrutinizer/" \                                -e "s/\${DATABASE}/scrutinizer/" \                                -e "s/\${HOST}/127.0.0.1/" \                                phpunit.xml.dist > phpunit.xml                            ./vendor/bin/phpunit \                                --verbose  \                                --stderr  \                                --coverage-clover build/logs/clover.xml \                                --coverage-text                        coverage:                            file: build/logs/clover.xml                            format: clover        analysis:            tests:                override:                    - php-scrutinizer-run                    -                        command: phpcs-run                        use_website_config: true    cache:        disabled: true        directories:            - vendor/filter:    excluded_paths:        - 'tests/*'

В некоторых случаях сборки могут падать из-за отсутствия phpcs в коде вашего репозитория, в таком случае добавьте этот файл.

phpcs.xml
<?xml version="1.0"?><ruleset>    <file>./</file>    <exclude-pattern>./vendor/*</exclude-pattern>    <exclude-pattern>./tests/*</exclude-pattern>    <exclude-pattern>./.github/*</exclude-pattern>    <rule ref="PSR1" /></ruleset>

А в ваш composer.json в секцию require-dev добавьте:

"squizlabs/php_codesniffer": "^3.5"

Начало работы

Теперь, давайте толкнем коммит в основную ветку и посмотрим, как выглядит Check Suite со Scrutinizer.

Пример корректной интеграции с GitHub

При желании эти проверки можно сделать обязательными (все или только часть) в настройках вашего репозитория на GitHub (в разделе настроек ограничений веток), - об этом я рассказывал в своем предыдущем посте про автоматизацию ручных действий с GitHub Actions.

Интерактив от Scrutinizer в ревью

После интеграции и успешно пройденной сборки в Scrutinizer появится три кнопки: Code Intelligence, Issues и Coverage.

По сути это визуализированный результат анализа вашего кода, очень помогает ревьюить, наверное...

А покрытие кода выглядит примерно так

При наведении на методы и переменные Code Intelligence подсвечивает типы возвращаемых значений, а зеленые маркеры справа от нумератора строк сообщают о покрытии тестами.

В целом, это удобно, хотя я к Code Intelligence еще не привык.

Итог

Я буду рад, если мой опыт был вам полезен и вот такие красивые отчеты вас будут мотивировать писать совершенный код.

Если у вас что-то не получилось с первого раза, не отчаивайтесь.

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

Ну а если, вы все же отчаялись и у вас ничего не получилось, мои контакты открыты для вас, напишите мне, и мы вместе решим вашу проблему.

Подробнее..

JuliaR преимущества интеграции

24.03.2021 08:16:47 | Автор: admin

R стабильный, удобный, имеет прекрасную экосистему. Один из признанных лидеров в области анализа и визуализации данных и, наверное, лучший инструмент для статистических исследований. Julia тоже удобна, весьма стройна и при этом ещё и быстра. Она ещё очень молода, не имеет такого количества пакетов и пользователей, но скорость работы впечатляет. Особенно по сравнению с R. Оба языка активно используются для анализа данных и машинного обучения. Что же выбрать?

Сопоставление производительности набора программ, реализованных на разных языках программирования, относительно производительности их реализаций на языке Chttps://julialang.org/benchmarks/Сопоставление производительности набора программ, реализованных на разных языках программирования, относительно производительности их реализаций на языке Chttps://julialang.org/benchmarks/

А что, если вместо неявно навязанного состязания выбрать оба языка и использовать преимущества и того, и другого? Ведь такая возможность есть. К тому же эта интеграция настолько проста, что особых усилий прикладывать и не нужно: несколько минут и всё готово.

Зачем вообще что-то интегрировать? Почему нельзя просто использовать R (Julia)?

Кратко рассмотрим преимущества обоих языков.

R удобен, стабилен, имеет богатую коллекцию пакетов на все случаи жизни. Он давно стабилизировался, поэтому его можно изучать просто по рецептам из сети и книг: они все работают. Есть много высококлассной литературы, статей, лекций, учебных курсов, что очень упрощает процесс освоения языка и дальнейшее совершенствование мастерства. Если не рассматривать случай каких-то очень больших массивов данных или сложных вычислительных экспериментов, то на производительность жаловаться не приходится, она для большинства задач вполне приемлемая. Поскольку язык интерпретируемый, все команды пользователя обрабатываются на лету, как правило без ощутимых задержек, что делает его очень удобным для разведочного анализа данных. Сочетание удобства языка и скорости выполнения команд дают ощущение взаимодействия с каким-то интеллектуальным ассистентом, который моментально выполняет все твои мысли. Это приятное ощущение.

Julia тоже удобна, тоже имеет приятный синтаксис, немного более строга к пользователю, но имеет свою особенную стройность. Из уникальных особенностей стоит отметить концепцию множественной диспетчеризации, возможность лёгкого доступа к генерируемому низкоуровневому коду, вплоть до ассемблерного, удобную систему макросов. Если бы этим качества и ограничивались, то она была бы просто аналогом R, но Julia при этом очень производительна, даже по меркам компилируемых языков. Вместе с С, C++ и Fortran Julia входит в престижный клуб языков с петафлопсной производительностью. Код на Julia можно запускать как на обычных многоядерных CPU, так и на GPU или вычислительных кластерах. Язык активно используется при проведении научных симуляций, обработке больших массивов данных, задачах машинного обучения. По сравнению с R аналогичные программы на Julia почти всегда в десятки-сотни раз быстрее. При этом благодаря JIT-компиляции команды выполняются почти на лету, как правило без серьёзных задержек. Не так молниеносно, как в R, задержки иногда всё же возникают, Julia как бы берёт некоторую паузу на подумать, особенно когда по ходу дела вдруг надо скомпилировать какой-нибудь пакет. Но подумав и перейдя к вычислениям, Julia работает с очень высокой производительностью. А существенные задержки, во-первых, случаются не так часто и поэтому не слишком раздражают, и к тому же при желании их можно максимально сократить, а во-вторых, ведётся работа над тем, чтобы повысить отзывчивость исполнения команд. Из-за сочетания производительности и удобства использования популярность Julia стремительно набирает обороты. В рейтингах TIOBE и PYPL язык уверенно движется к двадцатке лидеров и имеет вполне реальные шансы войти в неё уже в этом году. С августа 2018 года, когда вышла версия 1.0 и язык в значительной степени стабилизировался, прошло уже достаточно времени, чтобы он перестал ассоциироваться с чем-то сырым и постоянно меняющимся, появилась какая-то литература по актуальной версии. С другой стороны, многие рецепты из сети просто не работают, так как были опубликованы во времена старых версий и язык с тех пор уже претерпел многие изменения. Пока что по современным версиям языка очень мало литературы (на русском по версиям 1.0 и выше печатных изданий пока что вообще нет), нет такой прозрачной системы документации, как у R, далеко не для всех проблем можно найти в сети готовые красивые решения. С Julia постоянно чувствуешь себя первооткрывателем. Но это тоже приятное ощущение.

Сопоставление популярности языков R и Julia по данным рейтинга PYPLhttps://pypl.github.io/PYPL.htmlСопоставление популярности языков R и Julia по данным рейтинга PYPLhttps://pypl.github.io/PYPL.html

Что может дать использование Julia пользователям R

Julia явно быстрее, поэтому на ней разумно производить все ресурсоёмкие вычисления. Есть рецепты, как делать в R вставки на Fortran или C++, но по сравнению с ними Julia имеет гораздо более близкий к R синтаксис, вплоть до прямого заимствования отдельных команд из R. При этом по производительности она находится где-то на уровне близком к C и как правило обгоняет Fortran. В отличие от Fortran, С и C++ код в Julia компилируется на лету. Это позволяет сохранить высокую интерактивность работы, привычную после опыта в R. Вплоть до того, что в Atom и VS Code наиболее популярных средах разработки для Julia, можно точно так же, как в RStudio (наиболее популярной среде разработки для R) отправлять на исполнение произвольные куски кода по Ctrl+Enter. То есть общий подход к работе и ощущение от работы остаются примерно такими же, как и в R, но становится доступна очень высокая производительность.

У Julia есть потенциал, чтобы стать универсальным языком учёных будущего, каким раньше был Fortran. Похоже, ставка авторов Julia на сочетание скорости, удобства и открытости вполне оправдалась. Современный R несмотря на свою приятность сильно ограничен максимально возможной производительностью. Ради оптимизации под производительность приходится отказываться от циклов и писать все ресурсоёмкие процедуры в векторизованном стиле, из-за чего код становится тяжёлым для чтения и отладки. Но даже в максимально оптимизированном виде производительность далеко отстаёт от компилируемых языков. Ведётся работа над повышением производительности, но результат как правило нужен уже сейчас, поэтому приходится искать инструменты, которые прямо сейчас позволяют достичь его. Julia позволяет полноценно использовать циклы, предлагает простые средства для организации параллельных вычислений. Интересной особенностью экосистемы Julia являются интерактивные блокноты Pluto, позволяющие создавать красиво свёрстанные интерактивные научные презентации c формулами в LaTeX и выполняющимися на лету компьютерными симуляциями, меняющими своё поведение в зависимости от изменяемых в ходе показа параметров. Наверное, как-то так должны выглядеть научные публикации будущего.

Допустим, всё это звучит убедительно. Но выбрасывать все свои наработки на R ради освоения какого-то нового языка как-то не хочется. Хорошая новость в том, что если вместо перехода на Julia интегрироваться с ней, то можно ничего не выбрасывать, а всё так же, как и раньше, использовать все свои программы, шаблоны графики, подключаемые библиотеки. Всё это будет работать, как и раньше, и не потребует никаких модификаций.

Что может дать использование R пользователям Julia

Даже если Julia это универсальный язык учёных будущего, жить и работать учёным всё же приходится в настоящем. И в настоящем стоит отметить очень юный возраст Julia. Несмотря на то, что к настоящему времени экосистема языка и основных пакетов более-менее стабилизировалась, такого багажа наработок, который есть для R, удастся достичь ещё нескоро. R полезен хотя бы тем, что он используется как основной язык для демонстрации излагаемых концепций в учебных курсах и специализированной литературе, посвящённой решению конкретных прикладных задач. Есть прекрасные издания по анализу и визуализации данных, статистическому обучению, картографии и другим практическим областям, иллюстрированные примерами на R, в том числе - с использованием уникальных для R инструментов. В некоторых случаях подобные издания могут давать уникальную возможность получения знаний непосредственно из первых рук, от авторов предлагаемых методов. Можно подождать несколько лет, пока что-то подобное появится и для Julia, но при совместном использовании можно и не ждать, а использовать всё прямо сейчас.

Благодаря своим пакетам R давно уже превратился в своеобразный швейцарский нож, современная функциональность которого просто поражает. Вряд ли Julia в ближайшие годы сможет догнать R по количеству и качеству доступных пакетов, особенно в области статистики, анализа пространственных данных, картографии. Но зачем терять время, если все пакеты из R можно использовать уже сейчас? Да, их можно подключить и использовать практически как свои родные. В частности, это даёт возможность сопоставления результатов работы совсем недавно стабилизировавшихся пакетов Julia с результатами работы многократно проверенных пакетов R.

Отдельная тема - геоинформатика. Здесь R уже давно является одним из отраслевых стандартов. В некотором смысле, современный R с соответствующими пакетами можно рассматривать как открытую геоинформационную систему с текстовым интерфейсом, причём достаточно популярную и мощную. Вполне реальна ситуация, когда заходишь на какой-нибудь портал скачать необходимые для работы открытые пространственные данные, а там помимо готовой карты в pdf и самих данных лежит ещё и скрипт на R, который по ним строит правильно оформленную карту. Хотя в среде Julia тоже наблюдается определённое движение в направлении геоинформатики, до уровня R тут пока что ещё далеко. Но, опять же, при желании можно не ждать, а взять всё сразу. Взять всё сразу - это очень в стиле философии Julia.

Что для этого нужно сделать

Очевидно, есть два подхода к решению проблемы: вызывать код R из Julia и вызывать код Julia из R. Для вызова R из Julia есть специальный пакет Rcall. Для вызова Julia из R существует сразу несколько альтернативных решений: пакеты XRJulia, JuliaCall, RJulia и JuliaConnectoR. На данный момент наиболее прогрессивным из них выглядит JuliaConnectoR. Предлагаемый им подход немного тяжеловесен, и ориентирован скорее на использование отдельных функций из Julia, чем на полностью смешанное использование двух языков. Довольно любопытны доводы авторов в польу использования именно R, как связующей среды, опубликованные в их статье на arxiv.org.

Изначально я как раз планировал вызвать Julia из R. Потратил много времени, но так и не смог добиться работоспособности этой конструкции ни с XRJulia, ни с JuliaCall. Какой-либо информации про JuliaConnectoR тогда ещё не было, поэтому не было уверенности, что сама идея совместного использования этих двух языков - здравая. Всё шло к тому, что тяжёлые расчётные части придётся переписать с R на Fortran, с которым соприкасался когда-то в институте, но с тех пор никогда больше не использовал. Просто ради интереса решил попробовать вызвать R из Julia, и каково же было моё удивление, когда всё заработало практически из коробки и оказалось действительно удобным способом совместного использования двух языков. Возможно, это мой субъективный опыт, но именно на этом способе мне хочется сейчас остановиться. Про вызов Julia из R, возможно, расскажу как-нибудь в другой раз.

Итак, что нам понадобится:

1) Установленная Julia

2) Установленный R

3) В Julia необходимо установить пакет Rcall: Pkg.add("RCall")

4) Необходимо прописать путь до R. В Windows это удобнее всего сделать, задав переменную среды. В моём случае: R_HOME = C:\Program Files\R\R-4.0.3

Ну вот, собственно, и всё. Наш волшебный гибрид готов! Теперь в любом месте внутри кода Julia можно вызывать код на R и это будет работать. Можно даже не закрывать RStudio с запущенным сеансом в R. Вызов из Julia будет обрабатываться независимо от него.

Подключаем установленную библиотеку Rcall:

using RCall

Вызываем одиночную команду на R. Если R в результате выполнения команды должен построить график, то график откроется в отдельном окне:

R"plot(rnorm(10))"

Можно последовательно запускать отдельные команды, так, как будто мы работаем в консоли R, и они будут выполняться внутри единого сеанса:

R"var <- 5*8"

R"var"

R"var / 100"

Фактически это и есть сеанс в R, только обёрнутый в команды Julia. На каком языке в данном случае ведётся работа это скорее уже философский вопрос.

Естественно, дальше нам захочется как-то передавать переменные из одного языка в другой. Для этого удобно использовать команды @rput и @rget. Забрасываем какие-то переменные в R, что-то там считаем, забираем результаты обратно:

a = 1

@rput a

R"b <- a*2 + 1"

@rget b

Если необходимо выполнить последовательность команд, например целый лист кода, то удобнее использовать вставку вида:

R"""

"""

Например:

a = 1

@rput a

R"""

b <- a*2 + 1

c <- b*3

"""

@rget c

Внутри вызванного кода можно вызывать любые библиотеки R, и это будет работать так же, как и в обычном сеансе R.

Собственно, на этом всё. Никаких тайных знаний тут нет, можно брать любой код на R и запускать его из Julia. Для тех, кто программировал на R и только знакомится с Julia, это позволит осуществить процесс знакомства исключительно мягко. Начать можно с вызова имеющихся скриптов на R целиком, а затем уже, не торопясь, постепенно переписывать отдельные их части на Julia. При этом какие-то части можно так навсегда и оставить на R. Ну, или если очень захочется, со временем можно будет полностью мигрировать на Julia. Но даже в этом случае, вся мощь R и его пакетов всегда будет в шаговой доступности.

Есть ли здесь какие-то подводные камни? Я нашёл пока только два:

В текущей версии не поддерживаются кириллические комментарии в R. Придётся их поудалять или заменить на латинские.

В интерактивных блокнотах Pluto, силе и гордости экосистемы Julia, поддержка R работает странно. При первом запуске всё работает, но при изменении входных данных или содержимого R скрипта вместо обновления порождаемых им объектов происходит разрыв коннекта с R. Я пока что не нашёл способа, как обойти это ограничение.

Ещё небольшой комментарий: для того, чтобы комфортно работать с подобными двуязычными проектами, необходимо, чтобы среда разработки поддерживала оба языка и их совместное использование. Как Atom (прошлый фаворит Джулии, с которым она рассталась летом), так и VS Code (нынешний фаворит) при установке синтаксических дополнений для обоих языков такой режим поддерживают. Но очень может быть, что в каких-то средах такой поддержки не будет. Это может стать проблемой, потому что осуществлять навигацию по простыне кода, оформленного как один огромный комментарий, не очень удобно.

На этом всё! Надеюсь, это небольшая заметка была полезна

Подробнее..

API для бесплатной CRM

14.07.2020 14:04:30 | Автор: admin


Меньше года назад мы представили бесплатную CRM систему интегрированную с бесплатной АТС. За это время ей воспользовались 14 000 компаний и 64 000 сотрудников. Развитие ZCRM не прекращалось ни на минуту, появилось множество больших и маленьких функций.
Но мы понимаем, чтобы представить действительно функциональную систему, а не просто умную записную книжку, недостаточно только интеграции с телефонией. Сейчас мы предлагаем открытый API интерфейс, в котором доступно большинство функций ZCRM. API позволяет использовать CRM для любых каналов продаж.
Ниже кратко опишем работу с API и доступный функционал. Также приведен простой но полезный и рабочий пример: скрипт для создания лида из формы на сайте.

Кратко о бесплатной CRM


Воздержимся от объяснения что такое CRM. Бесплатная CRM Zadarma поддерживает все стандартные функции хранения данных о клиенте. Информация сохраняется в ленте клиента. Также кроме информации о клиентах доступен удобный постановщик задач с отображением на любой вкус (календарь, канбан, список). Все это доступно для 50+ сотрудников и полностью интегрировано с телефонией (в том числе звонки из браузера по технологии WebRTC).

Что значит бесплатная? Нет ни одного тарифа либо услуги ZCRM, за которые нужно платить. Единственное за что нужно платить это за телефонные звонки и номера (по спецтарифам, например, абонплата за номер Москвы 95 рублей или Лондона 1 евро). А если звонков почти нет? То и платить почти не нужно.
Бесплатная CRM активна пока активна бесплатная АТС Zadarma. После регистрации АТС активна 2 недели, в дальнейшем необходимо пополнять счет на любую сумму 1 раз в 3 месяца. Сложно представить офис, которому нужна CRM и АТС, но вообще не нужен ни номер ни звонки.

Зачем нужен API для бесплатной CRM


Чем больше контактов с клиентом тем лучше и контакты могут быть самыми разными. Благодаря API можно без проблем автоматически вносить (либо наоборот получить) информацию о клиенте/лиде и задачах. Благодаря этому появляется возможность подключить любые каналы общения с клиентами и любые другие системы автоматизации.
Благодаря API бесплатную ZCRM возможно использовать любым способом, как целиком так и частично. Например, как удобный интерфейс для работы с корпоративной базой клиентов, либо простой удобный планировщик.
Ниже приведем пример подобного канала подключение к CRM лид-формы на сайте. Позже на сайте приведем и другие примеры, например создание задачи перезвонить клиенту (отложенный звонок).

Основные методы API ZCRM


Так как в API ZCRM доступно 37 методов, воздержимся от описания всех, опишем лишь основные их группы с примерами.
Полный список с примерами доступен на сайте в описании API CRM.

Возможна работа со следующими группами методов:
  • Клиенты (общий список, отдельные выборки, редактирование, удаление)
  • Теги и дополнительные свойства клиентов
  • Лента клиента (просмотр, редактирование, удаление записей в лентах клиентов)
  • Сотрудники клиента (так как клиент как правило юридическое лицо, у него может быть не мало сотрудников)
  • Задачи (весь функционал по работе с задачами)
  • Лиды (аналогично все функции)
  • Пользователи СRM (отображение списка пользователей, их права, настройки, контакты и рабочие часы)
  • Звонки (возвращает список звонков)


Так как используется существующая структура API Zadarma, для нее на Github уже доступны библиотеки на PHP, C#, Python.

Пример использования API


Самый простой и при этом полезный пример создание лида из формы. Для минимизации кода этот пример содержит только основные данные лида. Подобный пример, но уже с комментариями от клиента (обычно присутствуют в каждой форме) доступен в блоге на сайте. Примеры скрипта написаны на PHP без фреймворков а потому легко встраиваются.
Пример html формы для создания лида:

<form method="POST" action="/zcrm_leads">   <label for="name">Name:</label>   <br>   <input type="text" id="name" name="name" value="">   <br>   <label for="phone">Phone:</label><br>   <input type="text" id="phone" name="phones[0][phone]" value="">   <br>   <label for="phone">Email:</label><br>   <input type="text" id="email" name="contacts[0][value]" value="">   <br>   <br>   <input type="submit" value="Submit"></form>


Данная форма крайне проста чтобы не перегружать статью. Она не имеет ни дизайна, ни капчи, ни поля с комментарием. В нашем блоге доступна версия с полем комментариев (комментарий добавляется в ленту клиента после создания лида).

И собственно PHP пример по созданию лида с данными из формы:

<?php$postData = $_POST;if ($postData) {   if (isset($postData['phones'], $postData['phones'][0], $postData['phones'][0]['phone'])) {       $postData['phones'][0]['type'] = 'work';   }   if (isset($postData['contacts'], $postData['contacts'][0], $postData['contacts'][0]['value'])) {       $postData['contacts'][0]['type'] = 'email_work';   }   $params = ['lead' => $postData];   $params['lead']['lead_source'] = 'form';   $leadData = makePostRequest('/v1/zcrm/leads', $params);   var_dump($leadData);}exit();function makePostRequest($method, $params){   // замените userKey и secret на ваши из личного кабинета   $userKey = '';   $secret = '';   $apiUrl = 'https://api.zadarma.com';   ksort($params);   $paramsStr = makeParamsStr($params);   $sign = makeSign($paramsStr, $method, $secret);   $curl = curl_init();   curl_setopt($curl, CURLOPT_URL, $apiUrl . $method);   curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');   curl_setopt($curl, CURLOPT_POST, true);   curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);   curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);   curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);   curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);   curl_setopt($curl, CURLOPT_POSTFIELDS, $paramsStr);   curl_setopt($curl, CURLOPT_HTTPHEADER, [       'Authorization: ' . $userKey . ':' . $sign   ]);   $response = curl_exec($curl);   $error = curl_error($curl);   curl_close($curl);   if ($error) {       return null;   } else {       return json_decode($response, true);   }}/*** @param array $params* @return string*/function makeParamsStr($params){   return http_build_query($params, null, '&', PHP_QUERY_RFC1738);}/*** @param string $paramsStr* @param string $method* @param string $secret** @return string*/function makeSign($paramsStr, $method, $secret){   return base64_encode(       hash_hmac(           'sha1',           $method . $paramsStr . md5($paramsStr),           $secret       )   );}


Как вы видите, работа с API достаточно проста, плюс присутствуют примеры работы на PHP, C#, Python. Таким образом без особых проблем можно вписать простую бесплатную CRM в любой рабочий процесс, получив автоматизацию малой кровью.
ZCRM непрерывно развивается и практически все новые функции будут доступны в том числе через API.
Также мы приглашаем интегрировать ваши существующие системы системы с бесплатными CRM и АТС Zadarma.
Подробнее..

История о том, как мы на 1С запилили продукт для интеграции с goods и не смогли остановиться

28.01.2021 18:09:16 | Автор: admin

Разработка и запуск нового продукта - это всегда риск. Зайдет, не зайдет. Все зависит от возможностей (люди, деньги), компетенций, опыта, да и в целом понимания, зачем все это делать. Даже самая детально проработанная стратегия может в итоге провалиться. Главное понимать, в чем реальная польза продукт, тогда и процесс разработки пойдет увереннее.

С таким подходом мы пришли в 2019 к разработке готового решения для автоматизации торговли на маркетплейсах - RDV Маркет. Сделали его на бессмертной системе 1С. Ее мы знаем от и до, и убеждены, что для запуска нового проекта с точки зрения бизнеса - это наиболее подходящий вариант. Но об этом позже. Пока расскажем, как все начиналось, и к чему в итоге привело. Погнали.

Что было в самом начале

Начали работу в 2018 году. Нас пригласили для интеграции goods с магазином М. Видео. Работу над процессом начали с построения архитектуры OMS (система управления заказами) вместе сразработчиками и аналитиками goods.

Наша команда выступала в качестве бизнес-консультантов. Мы прописывали ТЗ для проектной команды заказчика, строили алгоритм действий, что и как должно работать. На стороне M.Видео специалисты занимались подготовкой и выгрузкой товарных предложений (фидов), а сами заказы поступали через API goods.

Конечно, были баги. В процессе боевого тестирования интеграции заметили, что до маркетплейса систематически не поступает часть заказов, а до магазина не доходят конечные платежи. Чтобы разобраться в причине сбоя, вносили корректировки в OMS и дорабатывали вместе с аналитиками проекта биллинговую систему. В итоге исправили ошибки, и рабочий процесс пошел более или менее продуктивно.Но все равно большинство операций нужно было делать вручную, постоянно перепроверять каждый заказ, чтобы передавались корректные данные.

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

Почему 1С?

Итак, М. Видео продавал через goods, а мы вплотную занимались вопросом упаковки интеграции в отдельную автоматизированную систему. Долго не раздумывали, на чем будем делать - мы знали 1С, какие у нее есть возможности, поэтому решение запилили именно на ней. Плюсы у нее весьма ощутимые.

Во-первых, с типовыми конфигурациями работает большинство компаний. Таким образом, им будет проще начать работу с нашим решением, так как все будет происходить в их привычной "среде обитания". Все операции в общем окне 1С!

Во-вторых, система надежна. Ее уровня нагрузки достаточно для работы крупных компаний с количеством пользователей > 1000. В 1С можно спокойно систематизировать и масштабировать процессы под потребности компании.

Еще один важный момент помимо технической оснащенности, что 1С оперирует понятиями бизнес-объектов. Для выстраивания рабочих процессов с маркетплейсами это только в плюс.

Да, сам интерфейс привлекательным не назовешь. Не ждите от нас крутых скринов :) Тут 1С себе не изменяет. Но для нас в данном случае важнее всего все-таки возможности системы, ее адаптивность и гибкость.

Начали работу над RDV Маркет

У нас была четкая цель - получить решение, которое встраивается в 1С и автоматизирует процессы торговли на маркетплейсе. Все просто.

Что делали.

Шаг 1. Собрали обратную связь от продавцов goods, кто на какой учетной системе работает. Для этого сделали рассылку по базе и узнали, что у большинства установлены:

  • 1С:ERP;

  • 1С:Комплексная автоматизация;

  • 1С:Управление торговлей.

Была еще 1С УНФ (управление небольшой фирмой), но так как у нее мало функций под расширенную автоматизацию складских процессов, решили пока что ее не рассматривать. Но, возможно, придумаем что-то и с этой конфигурацией.

Шаг 2. Начали прорабатывать функционал и roadmap первой версии продукта. За основу взяли спринтовый вариант управления разработкой, с которым экспериментировали на goods. И где-то в течение первого полугодия выпустили первый релиз системы.

В функционал первой версии заложили автоматическую выгрузку товарного ассортимента в каталог (YML). Так как на примере goods поняли, что при увеличении количества товаров ручная работа с выгрузкой - это неэффективно, трата времени.

В планах было запустить 2 версии продукта. Взяли идею 1С про ПРОФ И КОРП. Собственно, отличия были соответствующие - в КОРП был шире функционал.

Помимо автоматической выгрузки фидов в первом релизе программы для ПРОФ было реализовано автоподтверждение заказов. До этого продавец мог только вручную пройтись по всем заказам. Да, если у него их всего 10 штук, то здесь проблемы нет. Но это такие редко встречаются.

Также в первой версии мы вплотную подошли к реализации функционала по автоматизации процесса комплектации заказов и учета отгрузки товаров.

Проблема задержки товаров на складе встречается довольно часто. При нарушении сроков отгрузки происходит блокировка аккаунта продавца, соответственно, тормозится процесс продаж, и компания несет убытки. Нам было важно сделать так, чтобы подобные ситуации сводились к минимуму, поэтому настроили единое место комплектации и отгрузки (APM кладовщика), тем самым сэкономили время на сбор и отправку заказов.

Над чем работали дальше, попутно закрывая баги

Несмотря на то, что первая версия на начальном этапе решала часть проблем с ручным вмешательством, ошибки и непонимание, как это сделать, а нужно ли это вообще, естественно, возникали.

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

В ходе рабочего процесса появлялись новые баги. Например, долго пилили финансовый модуль. Контроль взаиморасчетов изначально был довольно поверхностным, и много что приходилось делать руками. Мы хотели связать все финансовые операции по маркетплейсам, чтобы пользователь мог в одном месте видеть полную информацию по проведенным платежам, отправленным актам, а также за пару кликов вносить изменения.

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

Вопрос по работе с остатками стоял довольно остро. Разберем на простом примере. Магазин отправил на витрину 5 позиций, которые заказали покупатели. Но в это время в оффлайн уже было продано 2 товара из отправленных. Из-за того что на витрине маркетплейса никто не обновил остатки (в системе отображались только примерные данные), с маркетплейса было продано 5 товаров, но по факту в оформление ушло 3. То есть остальные заказы пришлось отменить. А с этим частить нельзя.

Рост отмен заказов отрицательно влияют на работу магазина, т.к. может произойти блокировка аккаунта (как и при сдвигах сроком отгрузки заказов). На сегодняшний день в текущей версии RDV Маркет реализовано автоматическое обновление остатков, что позволяет снижать или минимизировать % отмен, не доводя до критического момента.

В процессе работы над проектом пришла идея о возможности загрузки инфомоделей для товарного ассортимента (характеристики). Хотели сформировать в 1С инфомодели для каждого маркетплейса по всем категориям товаров, чтобы была возможность продавцам оперативно готовить свои каталоги и передавать их с минимум ошибок (в идеале - без) в маркетплейс. Но здесь не все так просто с получением подобного контента, поэтому идея пока висит - поставили ее в план на ближайшее будущее.

Не goods единым: с кем работаем сейчас

Goods развивался, привлекая все больше продавцов. А мы двигались дальше - хотели запартнериться с другими популярными маркетплейсами. На рынке уже активно вели свою деятельность Wildberries, Ozon и Беру (сейчас это Яндекс.Маркет). Мы начали с последнего. Причем начали довольно дерзко уверенно, сразу разместив информацию об интеграции с Беру у себя на сайте. Хотя на тот момент формального партнерства еще не было. Но так как их API была в открытом доступе, то мы успели все протестировать и настроить полноценную интеграцию с маркетплейсом.

Далее нас заметил Wildberries, который пришел к нам, когда продавал только по модели FBO (продажа со склада маркетплейса). Не так давно на WB случился прорыв - он заработал по схеме FBS (продажа со склада продавца). Здесь можно посмотреть, как мы настроили работу по новой схеме в 1С.

Чуть позже к нам присоединился Ozon - маркетплейс с непростой системой резервов. Площадка работает таким образом, что товар резервируется сразу, как только покупатель оформил заказ. Допустим, клиент неверно ввел данные карты, чтобы все исправить и оплатить покупку, ему дается 30 минут.

Вроде все ок. Но дело в том, что если продавец работает с несколькими каналами продаж, когда сезон высокого спроса, и допустим на складе не так много остатков, то при потоке заказов, менеджер маркетплейса может данный заказ отправить другому покупателю. И будет отмена. Мы настроили систему так, что таких потерь можно избежать - история резервов сохраняется в режиме онлайн,они формируются до того, как создался заказ, и продавец не превышает порог отмен.

Работаем с Wildberries, Ozon, Я.Маркет, goods, а недавно подключили AliExpressРаботаем с Wildberries, Ozon, Я.Маркет, goods, а недавно подключили AliExpress

Кликайте сюда, чтобы разобраться подробнее, что и как работает в RDV Маркет.


Что дальше? Идей много. Например, хотим сделать запуск решения по одному клику на кнопку, чтобы сократить количество шагов в настройках. А также смотрим в сторону реализации мобильного приложения и ребрендинга продукта.

В общем, работаем.

Готовы ответить на вопросы, подробнее рассказать про функционал - welcome в RDV Маркет!

Подробнее..

Перевод Шлюзы Java.Net в интеграционных продукциях InterSystems IRIS

06.10.2020 12:23:44 | Автор: admin

Шлюзы в InterSystems IRIS это механизм взаимодействия между ядром InterSystems IRIS и прикладным кодом на языках Java/.Net. С помощью шлюзов вы можете работать как с объектами Java/.NET из ObjectScript так и с объектами ObjectScript и глобалами из Java/.NET. Шлюзы могут быть запущены где угодно - локально, на удаленном сервере, в докере.

В этой статье я покажу, как можно легко разработать и контейнеризовать интеграционную продукцию с .Net/Java кодом. А для взаимодействия с кодом на языках Java/.Net будем использовать PEX, предоставляющий возможность реализовать любой элемент интеграционной продукции на языках Java/.Net.

Для нашего примера мы разработаем интеграцию с Apache Kafka.

Архитектура

Apache Kafka популярный брокер сообщений. В Kafka есть тема (topic) сообщения в которую издатели (publisher) пишут сообщения и есть подписчики (consumer) на темы, которые читают эти сообщения.

Сначала мы напишем Java бизнес-операцию которая будет пубиковать сообщения в Apache Kafka. Затем добавим бизнес-службу на языке C# которая будет сообщения читать, сохранять и передавать для дальнейшей обработки в InterSystems IRIS.

Наше решение будеть работать в докере и выглядит следующим образом:

Java Gateway

Прежде всего, разработаем Бизнес-Операцию на Java для отправки сообщений в Apache Kafka. Код может быть написан в любой Java IDE и выглядеть так:

  • Для разработки новой PEX бизнес-операции необходимо реализовать абстрактный класс com.intersystems.enslib.pex.BusinessOperation.

  • Публичные свойства класса это настройки нашего бизнес-хоста

  • Метод OnInit используется для установления соединения с Apache Kafka и получения указателя на InterSystems IRIS

  • OnTearDown используется для отключения от Apache Kafka (при остановке процесса).

  • OnMessage получает сообщение dc.KafkaRequest и отправляет его в Apache Kafka.

Теперь упакуем нашу бизнес-операцию в Docker контейнер.

Вот наш докер-файл:

FROM openjdk:8 AS builderARG APP_HOME=/tmp/appCOPY src $APP_HOME/srcCOPY --from=intersystemscommunity/jgw:latest /jgw/*.jar $APP_HOME/jgw/WORKDIR $APP_HOME/jar/ADD https://repo1.maven.org/maven2/org/apache/kafka/kafka-clients/2.5.0/kafka-clients-2.5.0.jar .ADD https://repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar .ADD https://repo1.maven.org/maven2/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar .ADD https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar .WORKDIR $APP_HOME/srcRUN javac -classpath $APP_HOME/jar/*:$APP_HOME/jgw/* dc/rmq/KafkaOperation.java &amp;&amp; \    jar -cvf $APP_HOME/jar/KafkaOperation.jar dc/rmq/KafkaOperation.classFROM intersystemscommunity/jgw:latestCOPY --from=builder /tmp/app/jar/*.jar $GWDIR/

Посмотрим, что здесь происходит (я предполагаю, что вы знакомы с многоступенчатыми докер сборками):

FROM openjdk:8 AS builder

JDK8 это базовый образ, в котором мы будем компилировать наше приложение.

ARG APP_HOME=/tmp/appCOPY src $APP_HOME/src

Копируем исходный код из папки /src в /tmp/app.

COPY --from=intersystemscommunity/jgw:latest /jgw/*.jar $APP_HOME/jgw/

Копируем библиотеки Java Gateway в папку /tmp/app/jgw.

WORKDIR $APP_HOME/jar/ADD https://repo1.maven.org/maven2/org/apache/kafka/kafka-clients/2.5.0/kafka-clients-2.5.0.jar .ADD https://repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar .ADD https://repo1.maven.org/maven2/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar .ADD https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar .WORKDIR $APP_HOME/srcRUN javac -classpath $APP_HOME/jar/*:$APP_HOME/jgw/* dc/rmq/KafkaOperation.java &amp;&amp; \    jar -cvf $APP_HOME/jar/KafkaOperation.jar dc/rmq/KafkaOperation.class

Все зависимости скачаны - вызываем javac/jar для компиляции jar файла. Для реальных проектов рекомендуется использовать полноценную систему сборки maven или gradle.

FROM intersystemscommunity/jgw:latestCOPY --from=builder /tmp/app/jar/*.jar $GWDIR/

И, наконец, jar файлы копируются в базовый образ Java шлюза, который содержит все необходимые зависимости и обеспечивает запуск Java шлюза.

.Net Gateway

Далее разработаем службу .Net, которая будет получать сообщения от Apache Kafka. Код может быть написан в любой .Net IDE и выглядеть так.

Особенности:

  • Для разработки новой PEX бизнес-службы необходимо реализовать абстрактный класс InterSystems.EnsLib.PEX.BusinessService.

  • Публичные свойства класса это настройки нашего бизнес-хоста

  • Метод OnInit используется для установления соединения с Apache Kafka, подписки на темы Apache Kafka и получения указателя на InterSystems IRIS

  • OnTearDown используется для отключения от Apache Kafka (при остановке процесса)

  • OnMessage получает сообщения из Apache Kafka и отправляет сообщение класса Ens.StringContainer в целевые бизнес-хосты продукции

Теперь упакуем нашу бизнес-операцию в Docker контейнер.

Вот наш докер-файл:

FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS buildENV ISC_PACKAGE_INSTALLDIR /usr/irissysENV GWLIBDIR libENV ISC_LIBDIR ${ISC_PACKAGE_INSTALLDIR}/dev/dotnet/bin/Core21WORKDIR /sourceCOPY --from=store/intersystems/iris-community:2020.2.0.211.0 $ISC_LIBDIR/*.nupkg $GWLIBDIR/# copy csproj and restore as distinct layersCOPY *.csproj ./RUN dotnet restore# copy and publish app and librariesCOPY . .RUN dotnet publish -c release -o /app# final stage/imageFROM mcr.microsoft.com/dotnet/core/runtime:2.1WORKDIR /appCOPY --from=build /app ./# Configs to start the Gateway ServerRUN cp KafkaConsumer.runtimeconfig.json IRISGatewayCore21.runtimeconfig.json &amp;&amp; \    cp KafkaConsumer.deps.json IRISGatewayCore21.deps.jsonENV PORT 55556CMD dotnet IRISGatewayCore21.dll $PORT 0.0.0.0

Посмотрим, что здесь происходит:

FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build

Используем образ .Net Core 2.1 SDK для сборки нашего приложения.

ENV ISC_PACKAGE_INSTALLDIR /usr/irissysENV GWLIBDIR libENV ISC_LIBDIR ${ISC_PACKAGE_INSTALLDIR}/dev/dotnet/bin/Core21WORKDIR /sourceCOPY --from=store/intersystems/iris-community:2020.2.0.211.0 $ISC_LIBDIR/*.nupkg $GWLIBDIR/

Копируем библиотеки .Net Gateway из официального образа InterSystems IRIS:

# copy csproj and restore as distinct layersCOPY *.csproj ./RUN dotnet restore# copy and publish app and librariesCOPY . .RUN dotnet publish -c release -o /app

Компилируем нашу бизнес-операцию.

FROM mcr.microsoft.com/dotnet/core/runtime:2.1WORKDIR /appCOPY --from=build /app ./

Копируем библиотеки в финальный контейнер.

RUN cp KafkaConsumer.runtimeconfig.json IRISGatewayCore21.runtimeconfig.json &amp;&amp; \    cp KafkaConsumer.deps.json IRISGatewayCore21.deps.json

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

ENV PORT 55556CMD dotnet IRISGatewayCore21.dll $PORT 0.0.0.0

Запускаем шлюз на порту 55556, слушаем все сетевые интерфейсы.

Готово!

Вот полная конфигурация docker-compose, для запуска демо целиком (в том числе и UI для Apache Kafka, для просмотра сообщений).

Запуск демо

Для запуска демо локально:

Установите:

Выполните:

git clone https://github.com/intersystems-community/pex-demo.gitcd pex-demodocker-compose pulldocker-compose up -d

Выводы

  • В интеграционных продукциях InterSystems IRIS появилась возможность создавать любые элементы продукции на языках Java/.Net

  • Код на Java/.Net возможно вызывать из InterSystems ObjectScript и наоборот, код на InterSystems ObjectScript из Java/.Net

  • Генерация прокси классов больше не требуется

  • Возможна как классическая поставка решения, так и поставка в Docker

Ссылки

Подробнее..

Личный опыт Свой среди чужих как встроиться в шведское общество

19.02.2021 00:11:18 | Автор: admin

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


Швеция считается самой закрытой страной среди европейских. Спросили инженера, который 2года назад уехал работать вSpotify, удалосьли ему вписаться вместную культуру, икак это сделать тем, кто только собирается переехать ксуровым северянам. Рассказывает Константин Голиков.





Как Костя оказался вШвеции


Привет, Хабр! Меня зовут Костя, вШвеции яживу два снебольшим года. Родился ивырос вСанкт-Петербурге, тамже начал строить карьеру. Перед тем, как окончательно релоцироваться, успел покататься вдлительные командировки вдругие страны Европы иСША. Поездка вШвецию вначале 2010-х была первым заграничным путешествием. Она так сильно отпечаталась впамяти, что сложно представить, вкакую другую страну ямогбы переехать.


Очутившись тогда вСтокгольме, япостоянно восхищался тем, как тут всё устроено. Везде велодорожки вРоссии оних десять лет назад ещё небыло речи, все люди красивые иухоженные нетолько женщины, ноимужчины.


Одним изсамых сильных впечатлений стало шведское метро. Меня поразило, что метро утилитарную вещь сделали таким красивым. Очень скандинавский подход подчёркивать красоту вмелочах иповседневности.


Мыжили наокраине Стокгольма, врайоне Bredng. В60-е тут построили армию многоэтажек, врамках программы Миллион домов. Это типичное советское жилье, хрущёвки ибрежневки, какие мывидим вРоссии, исвысоты птичьего полёта район кажется обычным российским город. Носмотришь наздания вблизи идумаешь: дочегоже здорово! Даже несмотря нато, что это панельки. Водворах, парадных, домах везде уют. Удивительно, как они сумели добавить комфорта таким прозаичным строениям этот аспект сильно запал вдушу.


Ияимею ввиду нетолько дизайн свизуальной точки зрения, ноипродуманность решений. Например, взять тотже ресайклинг как тут обращаются смусором. Несекрет, что насвалки отправляется 0,8% отходов. Почти укаждого дома есть площадки, где можно сдать бытовой мусор, рассортировав его надва вида стекла, пластик, бумагу, металл икартон. Ношведы неостановились наэтом: есть специальные пункты, куда можно принести что угодно, отстарой краски довешалок иботинок. Под каждый тип мусора свой контейнер. ВСтокгольме таких пунктов штук семь.


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


Тогда янедумал, что перееду сюда жить. Уменя никогда небыло специальной цели обязательно, вочтобы тонистало, эмигрировать.


Поэтому япосмотрел навсю красоту, скандинавский минимализм, дизайн, фику ибулки скорицей, иуехал обратно домой. Втот момент яработал впитерском Яндексе где-то около года, вырос отджуниора домидла.




Тасамая булочка скорицей

После этого перешёл вдругую компанию Exante, в2013 году она была единственной, которая занималась Scala. Это был пик технологий, вПитере, даивообще вРоссии, мало компаний втовремя использовали Скалу. Но, кроме языка итехнологий, это был любопытный опыт ещё ипотому, что Яндекс большая продуктовая компания, аExante маленький стартапчик. Дальше занимался вGrid Dynamics бэкендами, ипотихоньку начал трансформироваться вдата-инженера.


Мне был интересен челлендж, связанный сдата-инжинирингом. Объёмы данных часто титанические, номожно придумать алгоритм, как ихбыстро обработать или эффективно хранить. Плюс, вэтой области прилагаешь фундаментальные знания иполучаешь реальный эффект. Часто слышу, как люди жалуются, что университетские знания можно выкинуть напомойку, потому что приходится снова иснова заниматься поверхностными вещами: клепать одну итуже форму или один итотже запрос. Классно знать, что твои знания полезны идейственны это очень для меня важно.





Сгридами япроработал 5лет, было здорово ивнекотором смысле эпично. Это аутсорс, нонамой взгляд один излучших. Сменив несколько проектов, решил поискать, что ещё интересного есть под мой профиль вне гридов.


Ипока яобдумывал, что делать дальше, мне неожиданно написал бывший коллега (привет, Женя Муравьев, если тыэто читаешь), предложил свести состартапом взападной части Швеции, вмаленьком университетском городке Лунд. Это был стартап если правильно помню, занимались 3D-картами. Уже тогда ходили слухи, что иххотят купить Apple так витоге ивышло. Звучало дико интересно, номне почти сразу дали отворот поворот: Извини, парень, тынедостаточно сеньорен. Ребята, которые помогали сустройством, сказали, что несогласны сихмнением. Спустя пару лет они основали свой консалтинг, япереехал сними вСтокгольм.


После переезда выбор стоял между Spotify иDICE, номеня сразуже подкупили команда изадачи, которые ставили вСпоти. Когда вышел ссобеседования, честно говоря, думал, что всё пропало, все полимеры потеряны. Носпустя пару часов мне позвонили сказать, что берут меня.


Так ясначала оказался вШвеции, азатем вSpotify.


Как япредставлял интеграцию всообщество икакой она оказалась


Больших страхов небыло дорелокейта яуспел побывать вСтокгольме несколько раз изнал, чего ожидать. Новместе стем понимаю, какой это прыжок внеизвестность, особенно для тех ребят, кто видит новую страну либо наонсайте, либо уже после подписания контракта ипереезда. Дорелокейта мыпостоянно работали вместе скомандой изНью-Йорка, были длительные командировки вСША насчёт английского ябыл уверен, нооставались сомнения относительно шведского языка.


Думал, что впервый день выхода вофис будет очень неловко: все вокруг говорят по-шведски, яничего непонимаю, придётся просить говорить наанглийском. Как нистранно, ожидания неоправдались совсем. Вбольшинстве случаев ребята если иобщались ненаанглийском тонафранцузском, авовсе ненашведском.


Оказалось, вкоманде есть девушка изБельгии, парень изФранции, парочка шведов ия. Ноэти шведы учили французский вшколе икакое-то время жили воФранции поэтому легко нанего переходили. Немогу сказать, что это стало стеной, это были редкие эпизоды, когда они незамечали, что ярядом, после чего моментально переключались. Знаю, вдругих компаниях порой иностранцам приходится намекать, что они незнают шведского, ибуквально просить говорить наанглийском.


Аместные легко переключаются: они учат язык вшколе, ктомуже любое кино тут показывают воригинале. Есть мнение, что это стало возможным, потому что страна маленькая. Раньше 8миллионов жителей, сейчас 10, для них было неэффективно адаптировать внешний мир под себя, проще было выучить английский инетратить деньги напереводчиков. Незнаю, насколько это справедливо, нослышал такую версию.





Намоём опыте в90% случаев достаточно английского. Ноиногда всё-таки его нехватает. Например, вметро что-то сломалось. Порадио машинист передаёт сообщение, атынепонимаешь, что происходит. Можно спросить соседа повагону, онсогромным удовольствием переведёт иобъяснит, вчём дело. Носточки зрения быта, былобы удобнее понимать самому.


Ещё бывают затруднения при подписании контрактов. Идёшь вбанк, берёшь ипотеку, покупаешь жильё, снимаешь жильё все договора идокументы, что явидел, нашведском. Так что приходится либо подписывать наобум, либо просить заранее сделать электронную копию, чтобы перевести самостоятельно, либо берёшь знакомого шведа навстречу как переводчика.


Нуинаконец, невсе приложения переведены наанглийский. Например, есть местное приложение, похоже наТинькофф Инвестиции Банк Аванза, весь интерфейс нашведском. Выживать спомощью переводчика можно, ночувствуешь себя гораздо свободнее, когда знаешь местный язык. Так ислюдьми проще, ипонимаешь культуру.


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


При изучении шведского возникает другая проблема: если пытаешься нанём корявенько говорить, окружающие моментально переходят наанглийский, чтобы общение шло более эффективно. Хотя вмоём случае наоборот сомной сначала заговаривают нашведском, потому что явнешне похож нашведа. Язык немножко знаю, нобольше как пёс: всё понимаю, что говорят, ответить ничего немогу. Упартнёра опыт обратный: унеё яркая испанская внешность, инесмотря напрекрасное владение шведским, кней сначала обращаются наанглийском.


Немогу сказать, что ясупер-интегрировался ивсе мои друзья шведы. Думаю, естественным образом происходит так, что взаимодействуешь больше стакимиже приезжими, какты. Тут, наверное, стоит сделать отступление про шведскую культуру.


Местные очень любят домашний уют, проводить время ссемьёй идрузьями, комфорт воглаве угла. Иесли большую часть времени тыобщаешься суже сложившимся тесным кругом, конечно, нехочешь эту гармонию рушить. Поэтому вШвеции непринято брать навечеринку друга, который нискем совершенно незнаком: любой инородный элемент может разрушить баланс, ишведы нелюбят это делать.



Как вписаться вшведское общество


Получается так, что для того, чтобы попасть вшведское общество, нужно пройти, грубо говоря, некий первичный отбор, утебя две ноги, две руки иодна голова. Зачастую это происходит как раз врабочем контексте пообщался скакими-то людьми, ивот уже влиться внеформальное общение проще. Как нистранно, много шансов найти точки пересечения сместными, если тыпоёшь. ВШвеции очень популярны хоры: 600,000 человек повсей Швеции состоят вхорах. Вкультурных традициях присутствуют песни: например, вдень святой Люсии вСтокгольме прямо наулицах, иногда вофисах ишколах, устраивают шествия спеснями. Нарождественских застольях дело обстоит так. Собираются вместе семья, друзья, знакомые, сначала ешь, пьешь, апотом выпоёте песни иэто очень классно, чувствуется единение слюдьми вокруг. Поэтому если иностранец поёт это чит-код, аесли ходишь вхор ипоёшь вместе сошведами они тают.


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


Про клубы интеграции икурсы знаю, что они есть, ноявэто несильно погружался, незнаю, насколько они эффективны. Неуверен, что они сработают.


Когда начинаешь общаться сошведами, надо учитывать тот факт, что есть вопросы, накоторые можно ответить неправильно, иэто сразу 10 баллов квосприятию тебя. Вэтот список попадут темы изобласти нетерпимостей любая нетерпимость порицается, кроме, пожалуй, вопросов религии.


Местная церковь (Svenska kyrkan) весьма представлена, даже вбюрократическом смысле. Можно заявить, что являешься еёпоследователем, ивналоговой декларации под это будет отдельная строчка сдополнительным сбором нанужды церкви: 12 %. Ноесли составлять портрет среднестатистического шведа, ондовольно нерелигиозен обсуждать религию можно, это нечто-то сакральное. Явстречал шведов, которые жёстко шутили над некоторыми еёаспектами.


Политику, наверное, обсуждать нестоит, уместных есть небольшой уклон влевую сторону. Мне кажется, есть люди, которые, условно, поддерживают Трампа, ноони это неафишируют: некомильфо. Ещё одна трепетная тема иммиграция, обсуждать нюансы иммиграции очень большое табу.


ВШвеции это чувствительный топик. Например, районы Стокгольма, которые нароссийском телевидении обычно называют гетто, вофициальных бумагах иобсуждениях обозначаются зонами, которые требуют особого внимания. Это похоже надвойной язык: прямо никто неназывает, новсе понимают, очём речь. Вместе сэтим, конечно, стоит сказать, что вреальности это совсем нетак страшно выглядит, как показывают наТВ.


Новцелом для затравки обсуждать сошведами можно всё. Осенью, зимой ивесной можно смело начинать разговор стого, какже темно игрустно наулице. Можно говорить про хобби.





Содним иззнакомых мыкак-то разговорились ивыяснилось, что ондавным-давно ездил вРоссию, иэто было ещё вначале девяностых. Сам яслабо помню товремя, еслибы спрашивал своих родителей для них оно было чем-то естественным, непримечательным, атут максимально свежий взгляд состороны. Так, например, явспомнил тебочки сквасом, закоторыми сидела тётенька иналивала квас вобщую кружку. Ионподмечал много таких мелочей некоторые изкоторых янепомню вовсе (мне29, вначале девяностых яходил пешком под стол).


Или спорт. Другой парень, скоторым яобщаюсь, дикий фанат хоккея. Нооднажды яувидел его вфутболке Зенита, некоторые матчи которого онпосматривает пересечение поинтересам даёт большой буст вобщении.


Яупомянул темы, которые явно табуированы, аесть такие, которые шведы просто хотят выкинуть изголовы. Например, полиция прозрачна иинформативна, насайте полиции police.se есть вкладка, где показываются недавно совершённые преступления, вплоть доштрафов запревышение скорости. Если видишь наулице полицейские машины, сэтого сайта можно узнать, что насамом деле произошло. Швеция довольно спокойное место, ноиубийства, играбежи тут встречаются чаще, чем ихупоминают набытовом уровне. Пару раз япытался обсудить это сошведами, они вскользь бросают: Ну, наверное, это банды. Тоесть какой-то параллельный мир, вкоторый они нехотят соваться, что-то вне зоны комфорта, поэтому для них это как будто несуществует даже если стрельба была вцентре Стокгольма.


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


Например, здесь все обожают природу. Стокгольм идругие города очень ловко интегрированы вландшафт чтобы оказаться влесу, ненужно уезжать далеко. Доэтого мыжили наСёдермальме это один изцентральных островов Стокгольма. Нодаже там можно пойти впарк инаткнуться назайцев однажды мывидели ихтам влесу, чего ужговорить оболее отдалённых городских районах, где встречаются олени. Первое время мне это сносило крышу: центр города, парк некакой-то заповедник или зоопарк, откуда зайцы?!




Вид избиблиотеки

Иочень популярен хайкинг, навыходные можно куда-нибудь поехать. Полюбить хайкинг непункт изTo-do-списка, ноесли шведы регулярно показывают фотки, какие тропинки они нашли ипрошли, где были навыходных, начинаешь это делать сам ипостепенно вливаешься.



Как обстоят дела сейчас


Последнее время думаю, что коронавирус стал большой проблемой. Мыспартнёром изредка обсуждаем, как было здорово раньше, когда ейможно было ездить вуниверситет, мне наработу. Сейчас все сидят подомам. Общения стало меньше, восновном нынешние или бывшие коллеги, удевушки больше университетские друзья, некоторых подружек находила через приложения Bumble. Наверное, вцелом это описывает90% круга нашего каждодневного общения.


Чувствуюли ясебя интегрированным? Трудно сказать, это ведь как сосвободой. Когда она есть неощущается явно, когда еёстановится меньше резко понимаешь, чего нехватает. Янечувствую стеклянных стен вокруг, ситуации, когда ябы хотел проникнуть втусовку, аменя непускают. Может, это потому что мыживём вШвеции только два года ичерез пять лет япойму, что немогу продвинуться дальше какой-то точки.


Ноесть другая штука: если сравнивать всех моих друзей-знакомых вСтокгольме, французов, бразильцев, англичан, русских, среди них всех шведы самые интровертные. Внешне они отстранены, нокогда вывместе придёте вбар, ишведы немного выпьют понимаешь, что внутри они точно такиеже люди, какты, иэто просто внешнее впечатление.


Иногда кажется, что Стокгольму иШвеции нехватает некоторой разнузданности такой атмосферы, которую ячувствую, например, вБерлине, Нью-Йорке, Тбилиси или родном Питере, нопрактически неощущаю вСтоке. Всё стерильно ивылизано. Нотут трудно винить город, ивсегда можно сесть напоезд или всамолёт, ипросто получить свою порцию впечатлений.



Эмоции после переезда


Были вещи, которые меня шокировали, нонемогу сказать, что кним сложно привыкнуть. Например, как-то раз мыездили впригород Стокгольма, вышли изпоезда инаблюдали такую картину: идёт компания, трое мальчишек лет 1314. Они болтают, позади парень лет 35. Молодые ребята бросают бутылку, иследующий заними человек подбегает, вручает эту бутылку иговорит что-то вроде: Ребята, небросайте мусор. Уменя втот момент был культурный шок, как легко иорганично это было проделано. Неговорю, что вРоссии так никто неделает, новсё-таки совсем нетипичная картина.





Некоторые вещи после переезда дались тяжело. Это небыло сюрпризом, иязаранее знал обэтом, новШвеции нет такого гигантского разрыва между зарплатами программиста исреднестатистического жителя. Само собой, врезультате хоть иполучаешь больше, чем вРоссии, инахлеб смаслом хватает, вфинансовом плане это шаг назад, особенно напервых порах.


ВШвецию мыприехали втроём: я, партнёр икот, плюс пара чемоданов. Скотом процедура такая: нужно пройти покрасному коридору, подать документы ирастаможить кота. Мыприлетели ввосемь или девять часов вечера вАрланду, итаможня нас встретила закрытыми жалюзями. Что делать, непонятно: мысмотрим накота, кот смотрит нанас, другие люди бодро шагают взеленый коридор. Рядом сокошком висела трубкаи, как оказалось, достаточно позвонить, ипридёт сотрудник. Онглянул мельком: да-да, кот, вещи, проходите. Мне кажется, что ипечать поставил формально. Меня удивило ито, что коридор был закрыт, и, главное, скакой простотой онотнёсся ковсей ситуации. Вродебы, спросил, везёмли мыалкоголь, задал пару базовых вопросов, инаэтом всё. Эта простота общения сбюрократией мне показалась удивительной.




Говорим стаможенниками

Совет, который далбы себе два года назад


Совет номер один непереезжать вШвецию после сентября, это неплохо так портит впечатление впервое время. Самый тоскливый сезон сноября пофевраль. Стокгольм ився страна летом расцветают авот если после переезда сразу сталкиваешься сосенней хмурью, она невдохновляет. Плюс другая культура, всё непривычно, кафешки стеррасами закрыты, аесли кэтому добавляются темнота ихолод становится сложно. Переезжать вмарте проще, потому что видишь прекрасный город.


Для меня переезд небыл самоцелью, скорее приятным бонусом, частичкой авантюризма, было интересно пожить вдругом месте. Поэтому что ямогу сказать дерзайте, особенно если кэтому лежит душа :)



<рекламная пауза>
По статистике g-mate, минимум 3050% работодателей готовы рассматривать удаленку, а релокейт среди локаций на второй месте по популярности. И надоевший всем коронавирус не препятствие: за время пандемии и в России, и за рубежом наём ускорился в 3 раза.
Регистрируйтесь в @g_jobbot, подходящие вам вакансии с релокейтом будут приходить в Телеграм.
</рекламная пауза>
Подробнее..

Особенности национальной интеграции с платёжными системами

24.02.2021 12:05:30 | Автор: admin
Электронная коммерция стала трендом 2020 года. Крупные игроки рынка начали активно развивать сервисы доставки продуктов и готовых блюд. Как грибы после дождя выросли новые маркетплейсы. Даже те, кто был далёк от интернета и технологий, вынужденно погрузились в тему дистанционной торговли. Почему все знают, но сегодня поговорим не об этом. Перейдём сразу к ключевому звену коммерции приёму платежей. В статье поделюсь несколькими рекомендациями о том, как с ним работать.

image

Меня зовут Андрей Шубин, работаю в VK в команде разработки e-commerce в сентябре мы запустили Маркет ВКонтакте. Занимаюсь веб-разработкой десять лет, из них семь работаю с интернет-магазинами, маркетплейсами и другими проектами, продающими через сеть. Последние три года возглавлял разработку для электронной коммерции в компании Siberian Wellness у неё есть представительства в большинстве стран СНГ, ЕС, немного в Юго-Восточной Азии и Северной Америке. Именно из-за такой обширной географии мне довелось познакомиться с локальными платёжными агрегаторами, а также с законодательством некоторых стран.

Эта статья будет полезна разработчикам уровня middle, которые взялись за новое для себя направление ступили на скользкий путь e-commerce. Если вы в этой сфере давно, некоторые кейсы могут показаться очевидными. Буду рад любому фидбэку в комментариях.

Типы данных


Только ленивый не говорил, что вычислительная техника может допускать некоторую погрешность при работе с числами с плавающей точкой. Да-да, попробуйте сложить 0,1 и 0,2, если раньше этого не делали, будете удивлены. Но в работе с финансами точность и однозначность это то, что должно быть на первом месте. Если где-то в расчётах проскользнёт лишняя копейка, последствия могут быть печальными. Конечно, за одну копейку с вами связываться никто не будет хотя известны случаи, когда ФНС таки отправляла налогоплательщику требование уплатить недоимку в 0,01. Но при относительно больших оборотах разница между фактически полученной суммой и той, что отображена в чеках, может принять критическое значение и тогда знакомство с налоговым инспектором вам гарантировано. Особенно если всё происходит в Германии или Чехии.

Кстати, раз уж речь зашла о других странах и типах данных. В мире есть очень дешёвые валюты и с ними обороты компании могут измеряться суммами с целым паровозом нулей. В моей практике это были вьетнамский донг и белорусский рубль до 2017 года. Если вам предстоит работать с одной из таких валют, проверьте своё хранилище на предмет разрядности. Иначе может случиться то же самое, что сделал с ютубом клип Gangnam Style в декабре 2014-го.

Общение с платёжной системой


В общем случае жизненный цикл платёжной транзакции выглядит так:

  1. Принимаем заказ.
  2. Формируем ссылку на платёжный шлюз, в который вшиваем сумму к оплате, номер заказа и что там ещё попросит платёжная система.
  3. Редиректим клиента по этому адресу.
  4. Клиент вводит данные карты и перенаправляется на страницу благодарности (thank you page) магазина.
  5. Банк списывает со счёта клиента деньги и делает callback-запрос на специальный эндпоинт с нашей стороны.
  6. Меняем статус заказа и проводим попутные манипуляции.

Выглядит красиво и очень логично. Когда разрабатываешь систему, которая потенциально может быть заточена под торговлю на внешнем рынке, а то и не на одном, очень чешутся руки все процессы стандартизировать: создать такую библиотеку классов, чтобы для внедрения новой платёжной системы было достаточно дописать один. И здесь лежат очень коварные грабли: далеко не все платёжные системы считают, что описанная выше схема исчерпывающая и не требует изменений. Примеры? Их достаточно:

  • В Казахстане система не признавала редирект методом GET. Она требовала сначала на бэкенде сформировать зашифрованную строку с параметрами платежа, а затем нарисовать в магазине HTML-форму, где единственным параметром должна быть эта строка. И уже форму нужно было отправлять синхронно после этого пользователь попадал на страницу, где мог ввести номер карты. Но просто дождаться callback-запроса недостаточно: необходимо было сделать ещё один запрос с бэкенда, чтобы дать банку понять, что мы готовы принять оплату.
  • В Чехии ввели онлайн-кассы и из-за этого требовалось кроме суммы передать на сторону платёжного шлюза полный список позиций на чешском языке, включая цены и рассчитанные значения НДС. И если вы даёте клиенту скидку, то не можете просто взять и закинуть её в чек отрицательным числом. Так не работает. Нужно пересчитать стоимость всех позиций с учётом этой скидки или распределить её размер по конкретным позициям. И подойти к этому с умом потому что у разных товаров может отличаться ставка НДС, а если вы случайно дадите скидку на товары с большей ставкой, этим может заинтересоваться налоговый инспектор во время камеральной проверки.
  • Нацбанк Молдовы переложил на продавца обязанность отправлять клиенту электронный чек. При этом в нём требуется отображать определённую банковскую информацию. Получать её нужно из callback-запроса от банка либо отдельным методом API. А ещё местные ребята не особо следили за актуальностью документации и не могли ответить на наши вопросы в письмах так что пришлось ехать туда лично и общаться с техдиром.
  • В Узбекистане пошли дальше: решили, что один цикл запрос ответ это слишком просто. Считаем вместе:

    • раз банк узнаёт, готовы ли мы вообще принять платёж;
    • два банк передаёт ID покупателя, который мы ему передали раньше, а мы должны подтвердить, что это действительно наш пользователь;
    • три банк сообщает номер заказа и сумму к оплате, а мы проверяем, что всё ОК, и даём соответствующий ответ;
    • четыре банк подтверждает, что деньги успешно списаны, мы отвечаем, что приняли.

    И только в этот момент мы наконец меняем статус заказа у себя и движемся дальше. Четыре запроса вместо одного!
  • Вьетнамский оператор эквайринга вообще не присылает callback-запрос. Мы должны сами запросить статус транзакции. И при этом использовать ID транзакции, который присваивается оператором и передаётся GET-параметром на thank you page.

В общем, в сфере приёма платежей нет стандартных алгоритмов и похоже, что в ближайшее время они не появятся. Этот печальный факт не даёт нам прибить логику гвоздями и потом просто дописывать по одному файлику. Каждый новый платёжный оператор это новая документация, в которой авторы используют свою уникальную терминологию и представляют собственный взгляд на взаимодействие с продавцом. Фактически каждая интеграция это локальный апокалипсис, к которому надо быть готовыми.

Не ждите! Пишите первыми


Сделать на своей стороне эндпоинт, на который будет стучаться платёжная система со своими запросами, это очень заманчиво. Но будем честны никто из нас не в состоянии обеспечить 100%-й аптайм: в какой-то момент может отвалиться связь с сервером базы данных или с каким-то другим сервисом, и тогда ваше приложение не сможет отдать ожидаемый ответ. И здесь уже надо смотреть по документации, как поведёт себя конкретная платёжная система (если об этом, конечно, там написано).

Например, в том же Казахстане местный банк делал три запроса с разницей в секунду и после третьего фейла считал транзакцию неуспешной. При этом деньги клиента зависали где-то между счётом клиента и нашим р/с. Сдвинуть их в ту или иную сторону можно было двумя способами: либо делать отдельный API-запрос в течение ограниченного времени, либо идти в отделение банка, с которым заключён договор эквайринга, и писать заявление от руки. Знакомо?

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

Если такой возможности нет, то callback-запросы от банков лучше принимать на отдельный сервер, которому нужно обеспечить максимальный аптайм. В идеале это должен быть простой веб-сервер, который пишет таски в брокер сообщений. Совсем круто, если будет кластер таких серверов. Эх, мечты На самом деле такой монстр, скорее всего, не потребуется, но направление вы поняли.

Откройте глаза! Мониторинг наше всё


В работе с платежами есть скрытые процессы, от которых зависит вся цепочка электронной торговли. Чтобы не быть голословным, приведу пример из практики. У нас был договор интернет-эквайринга со Сбером и договор аренды онлайн-касс с Orange Data. Интеграции из коробки между этими двумя конторами нет, поэтому пришлось делать её на своей стороне. Обработали callback от Сбера, сформировали запрос к оператору фискальных данных, который генерирует чек и отправляет его в налоговую и клиенту на электронную почту. И вот в один не самый прекрасный день Orange Data аннулирует токен авторизации и чеки перестают выписываться и отправляться. Чтобы вы понимали, штраф за невыдачу одного чека 40000 рублей. А узнали мы о сбое в процессе совершенно случайно, спустя два месяца: один дотошный пользователь позвонил в контакт-центр спросить, почему ему не пришёл чек Спасибо, добрый человек! Мы успели всё пофиксить и разослать зависшие чеки до конца отчётного периода. А если бы не сделали этого, размер штрафов не оставил бы компании шансов на выживание.

Чем именно мониторить? Решайте сами. ВКонтакте мы используем Sentry и несколько крутых самописных инструментов. Но даже десятистрочный скрипт на питоне, отправляющий алярм в ваш любимый мессенджер, будет лучше, чем ничего. С деньгами шутки плохи их нужно контролировать максимально.

Иногда нужно поработать ручками


E-commerce, как и любой IT-продукт, это не только мы с вами (разработчики), но и целая инфраструктура: с юристами, региональными менеджерами, службой логистики и клиентской поддержки. Вот на последней остановимся подробнее.

Это ребята, которые принимают обращения пользователей через разные каналы. Причём обратная связь, как правило, негативная. И вот они весь день выслушивают претензии к работе вашего интернет-магазина, на них повышают голос в том числе за наши с вами баги.

А ведь большинства жалоб можно избежать: достаточно реализовать возможность вручную проводить манипуляции с заказами. Клиент оплатил покупку, и он вправе её получить и будет требовать этого. Так что ему безразлично, что вы технически не можете начать собирать заказ, пока не подтвердится оплата. Он будет звонить в контакт-центр, надеясь на решение, а получит дежурные фразы: технически невозможно, вы можете оставить претензию и перезвоните позже. Пожалейте коллег дайте им инструмент, устраняющий проблему клиента в один клик. Они будут вам благодарны и, может, как-нибудь в понедельник с утра угостят шипящей витаминкой.

Вместо заключения


Я описал далеко не все проблемы, с которыми мы сталкиваемся при работе в электронной коммерции. Не стал рассказывать и о курьёзных случаях в практике одна документация на монгольском языке чего стоит. А ещё совсем не затронул аспекты работы в правовом поле конкретных стран и многое другое об этом, возможно, поговорим в других моих статьях. Но надеюсь, что этот материал вам пригодится, сэкономит время и нервы. Если есть вопросы или хотите просто почитать умопомрачительные истории добро пожаловать в комментарии!

P. S. Обращаюсь от имени всех разработчиков из e-commerce к финтех-разработчикам. Ребят, ну сядьте вы уже, договоритесь, выработайте единый стандарт взаимодействия с мерчантом. Все же от этого выиграют!
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru