Интеграция с SAP ERP, на примере с Django-python, по протоколу oData(rest)

    Добрый день, Хабр!

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

    В частности, в моем примере будет описана интеграция SAP ERP с Django.

    Задача


    Из за введенного нашим любимым государством множества разных систем контроля: Егаис, Меркурий и многое другое, многие компании принялись адаптировать свои тяжелые, и мягко сказать неповоротливые системы (касается больших компаний) к новым условиям. Не буду говорить в каких в частности адаптировал и я, но в голове всегда крутилась мысль – Создать единую систему отслеживания всего на базе отдельной платформы.

    Средства


    Не особо долго выбирав какие инструменты взять, я выбрал: Язык программирования Python – ввиду обилия библиотек со всем и вся, платформу Django, вот не спрашивайте почему Django, а не Flask или Odoo. Odoo я уже брал за платформу и хотелось изучить одну из этих, взял первую, ну не знаю почему, наверное из за большей простоты. ERP систему для разработки SAP- ну тут у меня не особо был выбор, т.к. я работаю в компании интеграторе SAP, поэтому у меня есть и знания и доступ к песочницам этой системы, что бы имея все условия спокойно делать свое дело беспрепятственно.

    Frontend Django


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

    Вот первый ОЧЕНЬ грубый вариант описания программы.
    Исходящий процесс

    1) Создается исходящая поставка

    2) При проходе паллеты сквозь ворота — Автоматическая работа и полуавтоматическая работа

    a. Автоматическая работа/Когда паллет проходит сквозь ворота, программа запрашивает по RFC в WMS системе сведения о том, что это за поставка, ее номер и посылает обратно в WMS систему ответ о идентификации к поставке ( возможно подтверждает складские задачи(задания на комплектацию) по этой паллете и всем вложениям ). Также сверяет все акцизные марки со сведениями в WMS системе

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

    3) Отправляется состав поставки в Учетную систему

    Входящий процесс

    1) Создается входящая поставка

    2) Проход паллета сквозь ворота

    3) Посылается запрос к учетной системе о составе подлежащей приёмки на текущий склад

    4) Проверяется внутренний состав паллета акцизных марок на основании данной учетной системы

    5) Посылается сигнал в WMS системе о разгрузке паллеты.

    Необходимые таблицы:

    Ворота:
    Идентификатор;
    Склад:
    Описание.
    Сообщение о прохождении:

    Заголовок:
    Время, система, номер склада, идентификатор ворот.
    Позиция:
    Акцизная марка, время регистрации, привязка к заголовку
    Сообщение из ERP о составе (входящая поставка)

    Заголовок:
    Время, система, номер поставки,
    Позиция:
    Материал, акцизная марка, номер паллеты(если есть)

    Агрегированное сообщение (основанное на данных из ERP):

    Заголовок:

    Время, система, номер склада, идентификатор ворот, номер поставки из Учетной системы, Признак направления (Входящая исходящая), Признак сценария проверки, номер машины, номер ворот склада,

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

    class SapOptions(models.Model):
        name = models.CharField(verbose_name='Имя системы', max_length=50)
        baseurl = models.CharField(max_length=500, verbose_name='Url системы базовый', help_text = 'URL сервиса базовый, до класса, например :"https://moses1005:44300/sap/opu/odata/sap/ZLS_SUPPLYCHAIN_SRV/"')# Базовый URL 
        sapset = models.CharField(default='Enter Sapset', max_length=100, verbose_name='Имя (Сета)')
        mandt = models.CharField(max_length=3, verbose_name='Мандант')
        user = models.CharField(max_length=15, verbose_name='Имя юзера под которым будет выполнятся вход сервиса')
        passwd = models.CharField(max_length=15, verbose_name='Пароль')
        verify = models.BooleanField(default=False, help_text = 'Будет ли соединение безопасным')
        def __str__(self):
            return 'Имя: '+self.name + ', Мандант : '+self.mandt
    
    class Gates(models.Model):
        from mainAPP.sap_connector import get_lgorts_fromsap
        ident = models.CharField(verbose_name='Ворота', max_length=10, help_text='Склад',unique=True)
        wh = models.CharField(verbose_name='Ссылка на склад', default='',max_length=10, help_text='Связь со складов WMS')
        help = models.CharField(verbose_name='Описание', default='',max_length= 500,help_text='Описание ворот, где, для чего, на каком складе')
        try:
            lgorts = get_lgorts_fromsap()
        except:
            lgorts = [('No Connect', 'No Connect'),]
        lgort = models.CharField(verbose_name='Склад',default='0000', max_length=20, choices=lgorts)
    
        def __str__(self):
            return self.ident +' : '+self.wh+' : '+self.help
    

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

    Итак, после изучения Django (убил несколько вечером на это) я написал интерфейс для ввода информации и последующей отправки ее в SAP ERP.

    Первый экран ввода информации о приемке выглядит так:



    • Склад – прямая интеграция с SAP ERP с кратким описанием,
    • Поставка ERP – Вводится поставка из SAP ERP, при вводе верифицируется (происходит запрос в SAP, есть ли такая поставка или нет,
    • ТТН – тут все понятно (Товарно-транспортная накладная),
    • Номер партнера – Это номер партнёра SAP ERP, поле не обязательное, оно сделано на будущее, чтобы находить поставку,
    • Идентификатор упаковки – Это одно из самых важных полей, это номер паллета или упаковки.

    Также интерфейс адаптирован для мобильного терминала (ТСД)



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

    Далее после сканирования последнего поля или нажатию “Сохранить” экран переходит на диалог ввода идентификаторов каждого товара:



    Поля:

    1. Это экран списка идентификаторов, которые присланы самой системой ЕГАИС, где Красные, это еще не отсканированные идентификаторы, Желтые, это идентификаторы, которые были отсканированы, но в сообщении ЕГАИС их не было, и зеленые, это присланные ЕГАИС и были отсканированы
    2. Ввод идентификаторов, здесь так-же кнопка “+”, которая нужна, для появление еще одного поля и т.д.
    3. Вывод сообщений об ошибках, если они есть.

    Реализация интеграции:


    Для интеграции со стороны Django все понятно “rest” реализовать просто, а вот со стороны SAP ERP, пришлось немного почитать).

    Итак, как это делается, как оказывается не очень сложно

    1) Необходимо создать интеграционный класс для реализации, соответственно к нему пакет разработок. Делается это в транзакции SEGW



    2) После создания класса необходимо определить Data Model, тут есть несколько вариантов, создавать свои поля, или залайкать из SAP уже по таблице. Это значит, что перед тем как создавать модель данных для интеграции, необходимо создать таблицу для данных, это делается в транзакции SE11 и как это сделать можно найти на просторах интернета. Итак, лайкаем структуру,



    Я лайкаю уже созданную мной таблицу-структуру



    3) Вот так выглядит сделанная нами работа:



    Нажимаем “Сгенерировать” Класс сгенерировал необходимую для интеграции структуру, и с ней будем работать.

    4) Далее у нас во вкладке Service implementation появляется наша структура со всеми методами доступные ей, в частности:

    a. Create – Метод для создании записи в нашей таблице по присланным данным из вне
    b. Delete- Метод удалении записи по идентификатору
    c. GetEntity — Метод запроса одной записи
    d. GetEntitySet – Метод получения множества записей по критериям
    e. Update – Метод изменения записи
    На самом деле все эти методы достаточно абстрактны, но есть конечно и отличия

    5) После генерации класса у нас создается в ветке Runtime Artifacts список классов, выбираем тот, у которого на конце DPC_EXT


    Два раза щелкаем, что бы попасть в сам класс

    6) После того, как попали в список методов класса, в конце вы видите список всех методов, обязательно переопределите его, иначе после очередного изменение модели данных, у вас все сотрется, я столкнулся с этим, было обидно…


    Для примера покажу реализацию метода Create



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

    7) Тестируем наш интерфейс в транзакции /IWFND/MAINT_SERVICE, длинная такая. Заходим в нее, находим созданные наш класс и нажимаем “Клиент шлюза SAP”



    Нам открывается по сути ЭМУЛЯТОР GET\POST\PUT\DELETE web Запросов, только от SAP,

    P.S. Можно в чем угодно тестировать созданный сервис., я тестирую в программе postman



    Так выглядит запрос get, “GetEntitySet”
    /sap/opu/odata/sap/ZLS_SUPPLYCHAIN_SRV/ZLS_INBOUND_HEADSet?$format=json
    Где:
    /ZLS_SUPPLYCHAIN_SRV/ — Это наш созданный класс
    /ZLS_INBOUND_HEADSet – это созданная нами модель данных,
    format=json – это формат данных, который мы получаем, выбор xml или json, я выбираю json, потому что вот почему, мне так удобнее.

    8) Аналогично пишем методы

    Что мы имеем, создали фронт на Django, создали интерфейс на стороне SAP
    Теперь нам необходимо это все оживить, и именно на стороне Django пишем методы:

    1) Метод создания сессии, для того, чтобы залогинится, получить scrf-token и уже дальше
    дергать нужную информацию из БД по нашему настроенному интерфейсу или создать новую
    запись. Для этого создаем отдельный файл в Django, я назвал его Sap_connector.py и описал
    в нем основные методы.

    def sap_createSession(): # Создание сессии для запросов oDATA
        from scanner.models import SapOptions
        # Конектимся к SAP
        sap_opt = SapOptions.objects.all()[0] # Получаю из локально бд данные коннекта
        s = requests.Session()
        s.headers.update({'Connection': 'keep-alive', 'X-CSRF-TOKEN': 'Fetch'})
        auth = (sap_opt.user, sap_opt.passwd)
        try:
            r = s.get(sap_opt.baseurl, auth=auth,verify=sap_opt.verify)
        except:
            message = "Нет соединения с системой %s  %s"%(sap_opt.mandt, sap_opt.name)
            return ('NO TOKEN', 'NoSession', message)
        token = r.headers['x-csrf-token']
        session = s
        sess = (token, session, None)
        return sess
    

    2) Метод верификации поставки в SAP ERP

    def sap_delivery_verify(token, session, delivery): # проверить есть ли поставка в ERP
        from scanner.models import SapOptions, Gates
        sap_opt = SapOptions.objects.all()[0]
        s = session
        format = '?$format=json'
        set = 'likpSet'
        url = sap_opt.baseurl + set + "('"+delivery+"')"+format
        headers = {'Content-type': 'application/json;charset=utf-8', 'X-CSRF-TOKEN': token} # обновляем хидер и добавляем токен
        auth = (sap_opt.user, sap_opt.passwd) #для auth
        get = s.get(url, headers=headers, auth=auth,verify=sap_opt.verify)# Запрос данных
    
    
        if get.status_code ==200:
            delivery_out = json.loads(get.text).get('d').get('Vbeln')
            return (True, 'OK')
        else:
            error_text = json.loads(get.text).get('error').get('message').get('value')
            return (False, error_text)

    3) Метод интеграции складов SAP с Django

    def get_lgorts_fromsap():
        from scanner.models import SapOptions
        session = sap_createSession() # Создаю сессию
        token = session[0]
        session = session[1]
        sap_opt = SapOptions.objects.all()[0] # Получаю настройки
        s = session
        format = '?$format=json'
        set = 't001lSet'
        url = sap_opt.baseurl + set +format
        headers = {'Content-type': 'application/json;charset=utf-8', 'X-CSRF-TOKEN': token} # обновляем хидер и добавляем токен
        auth = (sap_opt.user, sap_opt.passwd) #для auth
        get = s.get(url, headers=headers, auth=auth,verify=sap_opt.verify)# Запрос данных
        jdata = json.loads(get.text)
        lgorts = []
        for l in jdata.get('d').get('results'):
            l.get('lgort')
            lgorts.append((l.get('Lgort'),l.get('Lgort')))
        return lgorts
    

    И его вторая часть в части models:

    class Gates(models.Model):
        from mainAPP.sap_connector import get_lgorts_fromsap
        ident = models.CharField(verbose_name='Ворота', max_length=10, help_text='Склад',unique=True)
        wh = models.CharField(verbose_name='Ссылка на склад', default='',max_length=10, help_text='Связь со складов WMS')
        help = models.CharField(verbose_name='Описание', default='',max_length= 500,help_text='Описание ворот, где, для чего, на каком складе')
        try: #Получаем склады из SAP
            lgorts = get_lgorts_fromsap()
        except:
            lgorts = [('No Connect', 'No Connect'),]
        lgort = models.CharField(verbose_name='Склад',default='0000', max_length=20, choices=lgorts)
    
        def __str__(self):
            return self.ident +' : '+self.wh+' : '+self.help
    

    Интеграция выглядит так: когда я в настройках Django создаю новый склад, то в поле склад система мне выводит непосредственно склады из SAP ERP, которые в данный момент там созданы.



    4) Метод создания новой записи в SAP ERP

    def sap_connect(token, session, data):
        from scanner.models import SapOptions, Gates
        # Запись идентификаторов в базу данных SAP ERP
        sap_opt = SapOptions.objects.all()[0]
        s = session
        delivery = data.get('delivery')
        url = sap_opt.baseurl + sap_opt.sapset # Базовая URL + сет для подключения
        headers = {'Content-type': 'application/json;charset=utf-8', 'X-CSRF-TOKEN': token} # обновляем хидер и добавляем токен
        auth = (sap_opt.user, sap_opt.passwd) #для auth
        data =json.dumps({"d":{
            "Mandt": sap_opt.mandt,
            "Lgort": Gates.objects.get(id=data.get('gates')).wh,
            "Vbeln":data.get('delivery'),
            "Mark": data.get('mark'),
            "Matnr": "",
            "Posnr": "",
            "Mbl": "",
            "InbDicid":"AAAAAAAAAAAAAAAAAAAAAA==",
            "DocExt": data.get('ttn'),
            "Exidv": data.get('pack')
        }})
        try:
            r2 = s.post(url, data=data, headers=headers, auth=auth)
        except:
            return (False, 'Нет соединения с системой')
    
        if r2.status_code ==201:
            print('Поставка %s Записана в БД'%(delivery))
            return (True, 'для поставки %s : '%delivery)
        else:
            print('Поставка НЕ %s Записана в БД'%(delivery))
            text = r2.text
            SO = soup(text)
            s = SO.find_all("message")[0].text
            return (False, s)

    По сути мы просто делаем запрос POST и записываем туда данные в формате json

    Заключение


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

    В итоге имеем программу обработки входящих и исходящих идентификаторов продукции в компании, при этом 90% всей работы проводится именно во внешней системе, интегрированной с основной системой.

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

    P.S. Я не расписывал код ABAP или python-django данного рабочего решения, не расписывал настройки Django или html теймплейтов, а сконцентрировался на интеграции с SAP ERP, что бы показать, что создание модуля для подключения к такой крупной системе как SAP достаточно не сложно и у меня ушло на такую систему, если включить еще изучение Django около 4х вечеров.

    Спасибо всем за внимание, за конструктивную критику буду благодарен!
    Share post

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 23

      0
      Прекрасно, прекрасно.

      А такого рода запросы
      SapOptions.objects.all()[0]

      сейчас модно писать так
      SapOptions.objects.first()
        0

        Спасибо, учту, я далёк от уровня pro в Django и python )

          0
          А чем вам не понравилось Odoo? Мне оно тоже не нравится, но хочется услышать мнение человека поработавшего с несколькими ERP.
            0

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

              0

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

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

          Я только не понял, зачем вообще нужна эта система, если вся информация уже есть в ERP. Что мешает сделать ввод данных о приходе/уходе товара прямо там? Есть какие-то сложности?

            0

            Самое очевидное это, когда в компании не одна Система, а целый набор,
            Так-же ее не нужно рассматривать как трекинг системой за егаис, это скорее аналог системы track and trace, единая система следящая за идентификаторами, ЕГАИС, Меркурий, лекарства, партии и проч. Я думаю скоро на все введут UiN номера.

              0
              Я думаю скоро на все введут UiN номера.

              Что это такое?

              Подскажите, на каком SAP ERP всё проделали, какие EHP? Бегло потыкал в наш SAP, у нас нету: /IWFND/MAINT_SERVICE

              Спасибо за пост!
                0
                Должен быть установлен компонент
                SAP_GWFND 752 0001 SAPK-75201INSAPGWFND SAP Gateway Foundation
                  0
                  Благодарю! Про UiN, подскажите пож-та, о чём это?
                    0

                    Uncial identification number
                    Номер уникальный к каждой единицы продукции.
                    На сколько известно моей фирме, эта тенденция идёт с важной продукции, лекарства, алкоголь, мясо. А скоро будет и молочка, одежда и пока хватит безумия государства

                      0
                      Понял… Во истину безумие. В общем, чтобы отслеживать конкретную пачку макарон по этому делу…
                        0
                        Про макароны, а как еще), они же сделаны из пшеницы, а эта пшеница должна быть сертифицирована, а сертификат подразумевает разрешение на потребление людям, или животным).
                        Как минимум идентификатор того, из какой партии сделаны макроны на пачке должен быть и отслеживаться в плоть до поля, с которого было собрано зерно
                          0
                          Слышали что ответили производители молока на Меркурий? Слышали как он там эпично провалился? Они, собственно, заявили, что смешивают молоко с разных ферм в один чан, и им отменили Меркурий.
                            0
                            Я вижу тенденцию. Проработают Меркурий так, что бы все нюансы учитывались, или заставят производителей не смешивать. Главное не то, что происходит сейчас, а куда все идет.
                              0
                              С Меркурием на самом деле всё намного хуже, чем вы представляете. процентов 50 предприятий гонит туда полное фуфло…
                                0
                                Увы, согласен. Больше всего расстраивает то, что за все эти (в общем то, бесполезные) новшества, платить будем мы.

                                Спасибо за ответы!
                                  0

                                  Вам спасибо)

                0

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

                0
                A почему выбрали Django, а не родной для SAP UI5?
                  0

                  Было решено, что на Django разработка будет быстрее и дешевле. Так оно и вышло.

                Only users with full accounts can post comments. Log in, please.