Выгрузить данные из МДЛП: авторизация, интеграция API Честного знака, примеры на C# - Анализ МДЛП от super.web
Самара, ул. Советской Армии, 253, оф. 4
Назад к статьям

Выгрузить данные из МДЛП: авторизация, интеграция API Честного знака, примеры на C#

Фармацевтические компании, работающие с маркированной продукцией, сталкиваются с необходимостью интеграции своих внутренних систем с системой МДЛП (Честный знак) для автоматизации отчетности, анализа эффективности медпредставителей и/или контроля оборота.

Если ваша внутренняя система работает на .NET, отличным выбором для начала станет библиотека MdlpClient на GitHub, реализующая полноценный доступ к API Честного знака. В этой статье поделюсь своим опытом, как программно выгружать данные из МДЛП и интегрировать с внутренними ИТ-системами.

1. Понимание структуры данных МДЛП

Для начала необходимо изучить нужную часть API МДЛП и информацию по требуемым справочникам. Есть очень важный и подробный документ Описание API МДЛП, им вам скорее всего придётся часто пользоваться (нам приходилось). В нашей разработанной системе основной отчёт — “Общий отчёт по выбытию”. Часто запрашиваемые функции: выгрузить реестр КИЗ, получить историю по SGTIN, отслеживать статусы. Почти полный список обрабатываемых нами отчётов из МДЛП:

ПИК-ФАРМА-интерфейс

2. Доступ к API и авторизация

Каждый запрос к API требует авторизационный токен. Основной используемый нами эндпоинт — api.mdlp.crpt.ru. Получить токен может только тот, у кого есть ЭЦП, т.е. заказчик, сама фармкомпания, а именно сотрудники имеющие доступ. Время жизни токена — 12 часов, это сделано для безопасности и мы уважаем такой подход. Для корректной работы с API на машине должны быть установлены сертификаты МинЦифры и ГОСТ-шифрования, а также КриптоПро CSP. Подробнее в той же документации стр. 30 пункт 4.5.

Итак, вы разработчик у вам поставлена задача разработать программу автоматической выгрузки данных. Первый запрос в API когда вы пришли утром в офис и налили себе кофе – запрос на авторизацию, используя сертификат подписи ЭЦП (носитель в момент запуска должен быть подключен к компьютеру-клиенту). Для получения авторизационного токена в примере кода используется библиотека yallie/MdlpClient (GitHub), как было указано ранее. Эта библиотека предоставляет необходимые функции для взаимодействия с API:

var credentials = new ResidentCredentials
{
    ClientID = ConfigurationManager.AppSettings["ClientID"],
    ClientSecret = ConfigurationManager.AppSettings["ClientSecret"],
    UserID = ConfigurationManager.AppSettings["UserID"]
};

_client = new MdlpClient(credentials: credentials)
{
    Tracer = Trace
};

var authToken = credentials.Authenticate(_client);
var token = authToken.Token;//это и есть нужный нам для запросов токен

 

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

Можно всё проверить и отладить в песочнице с тестовыми сертификатами, это отдельная ссылка на API для разработки. В официальной документации про это написано, а также в yallie/MdlpClient.

3. Пример запроса для получения данных

Когда мы получили токен, создать уже авторизованного клиента API можно так:

var credentials = new ResidentCredentials
{
    ClientID = ConfigurationManager.AppSettings["ClientID"],
    ClientSecret = ConfigurationManager.AppSettings["ClientSecret"],
    UserID = ConfigurationManager.AppSettings["UserID"]
};

var client = new MdlpClient(credentials: credentials);

// авторизовываем юзера вручную
client.Client.Authenticator = new CredentialsAuthenticator(client, credentials, token.ToString().ToLower());
client.IsAuthenticated = true;

 

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

PublicBranchFilter filter = new PublicBranchFilter()
{
    Inn = inn
};

if (Guid.TryParse(partnerMDCode, out var o))
{
    filter.SysID = partnerMDCode;
}
else
{
    filter.BranchID = partnerMDCode;
}

var result = client.GetBranches(filter, 0, 10);

var address = result.Entries.Select(x => x.Address).FirstOrDefault(x => x.AddressDescription != "Не найдена действующая лицензия по данному адресу");

addressFias = address?.HouseGuid;
return address?.AddressDescription;

 

Учитывайте что API МДЛП контролирует нагрузку и не даёт обращаться чаще чем в ~5 секунд.

Большинство данных большого объёма, поэтому используется задача на выгрузку, а когда она готова, скачиваем результат, вот пример для Общего отчёта по выбытию ЛП:

taskType = ScheduleTaskType.GENERAL_REPORT_ON_DISPOSAL;

//{"1026_IC_Period_Type_WM":"IC_Period_Month","1028_IC_Period_Week":null,"1027_IC_Period_Month_11_2019":"24275","1156_IC_Product_MDLP_general_gtin":["1234567890","12345678901"],"1157_IC_Product_MDLP_general_batch":null}
taskId = client.CreateNewUnloadingTask(taskType, taskParams);

//опрашиваем раз в выбранный период времени, например каждые 5 минут
UnloadingTaskStatus taskStatus = 
    client.GetAnalyticUnloadingTaskStatus(taskId);

if (taskStatus == UnloadingTaskStatus.COMPLETED)
    actualTask.Status = (short)MDLPTaskStatus.WaitingUnloading;

else if (taskStatus == UnloadingTaskStatus.FAILED)
    actualTask.Status = (short)MDLPTaskStatus.Failed;

//получаем zip архив с cvs файлом внутри
byte[] result = client.DownloadFile("export/tasks/{task_id}/result", $"report-{taskId}", new[]
{
    new Parameter("task_id", taskId, ParameterType.UrlSegment),
});

 

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

Для примера выше вам понадобится ещё вот этот код, мы дополняли библиотеку своими классами и методами:

/// <summary>
/// 11.1. Метод создания нового задания на выгрузку
/// </summary>
[DataContract]
public class CreateUnloadingResponse
{
    /// <summary>
    /// Идентификатор созданной задачи
    /// </summary>
    [DataMember(Name = "task_id", IsRequired = true)]
    public Guid TaskId { get; set; }

    /// <summary>
    /// Дата создания задания
    /// </summary>
    [DataMember(Name = "create_date", IsRequired = true)]
    public DateTime CreateDate { get; set; }
}

/// <summary>
/// 11.1. Метод создания нового задания на выгрузку
/// </summary>
[DataContract]
internal class TaskCreatingParams
{
    /// <summary>
    /// Тип задания на выгрузку
    /// </summary>
    [DataMember(Name = "report_id", IsRequired = true)]
    public string ReportId { get; set; }

    /// <summary>
    /// Параметры фильтрации создаваемого задания
    /// </summary>
    [DataMember(Name = "params", IsRequired = true)]
    public GENERAL_REPORT_ON_DISPOSAL TaskParams { get; set; }
}

/// 11.1. Метод создания нового задания на выгрузку. Метод класса MdplClient
/// </summary>
/// <param name="reportType">Тип задания на выгрузку</param>
/// <param name="taskParams">Параметры создаваемого задания</param>
/// <returns>Id созданной задачи</returns>
public Guid CreateNewUnloadingTask(ScheduleTaskType reportType, GENERAL_REPORT_ON_DISPOSAL taskParams = null)
{
    RequestRate(60);

    var reportId = reportType.ToString();

    return Post<CreateUnloadingResponse>("data/export/tasks", new TaskCreatingParams
    {
        ReportId = reportId,
        TaskParams = taskParams
    })
    .TaskId;
}

[DataContract]
public class AnalyticUnloadingStatusResponse
{
    /// <summary>
    /// Идентификатор созданной задачи
    /// </summary>
    [DataMember(Name = "id", IsRequired = true)]
    public Guid TaskId { get; set; }

    /// <summary>
    /// Время хранения выгрузки в днях
    /// </summary>
    [DataMember(Name = "type", IsRequired = true)]
    public string TaskType { get; set; }

    /// <summary>
    /// Статус задачи
    /// </summary>
    [DataMember(Name = "status", IsRequired = true)]
    public UnloadingTaskStatus CurrentStatus { get; set; }

    /// <summary>
    /// Даты создания задачи
    /// </summary>
    [DataMember(Name = "create_date", IsRequired = true)]
    public string CreateDate { get; set; }

    /// <summary>
    /// Количество выполненных / общее количество
    /// </summary>
    [DataMember(Name = "progress", IsRequired = true)]
    public string Progress { get; set; }

    /// <summary>
    /// Параметры задания в формате *.json
    /// </summary>
    [DataMember(Name = "parameters", IsRequired = false)]
    public object TaskParams { get; set; }

    /// <summary>
    /// Используемый язык
    /// </summary>
    [DataMember(Name = "language", IsRequired = true)]
    public string Language { get; set; }
}

/// <summary>
/// 10.2 Получения статуса аналитической выгрузки (не путать с 11.2)
/// </summary>
public static UnloadingTaskStatus GetAnalyticUnloadingTaskStatus(this MdlpClient client, Guid taskId)
{
    client.RequestRate(60);

    return client.Get<AnalyticUnloadingStatusResponse>("export/tasks/{task_id}", new[]
    {
        new Parameter("task_id", taskId, ParameterType.UrlSegment),
    })
    .CurrentStatus;
}

/// <summary>
/// Performs GET request and returns a string. MdplClient class method
/// </summary>
/// <param name="url">Resource url.</param>
/// <param name="fileName"> Name ov saving File </param>
/// <param name="parameters">IRestRequest parameters.</param>
/// <param name="apiMethodName">Strong-typed REST API method name, for tracing.</param>
public byte[] DownloadFile(string url, string fileName, Parameter[] parameters = null, [CallerMemberName] string apiMethodName = null)
{
    var request = new RestRequest(url, Method.GET);
    if (!parameters.IsNullOrEmpty())
    {
        request.AddOrUpdateParameters(parameters);
    }
    request.AddHeader("Accept", "*/*");
    request.AddHeader("X-ApiMethodName", apiMethodName);

    var data = Client.DownloadData(request);
    return data;
}

 

Собственно дальше надо переменную result разархивировать и обработать полученную таблицу по вашей задаче.

🛠 4. Интеграция в вашу систему

Вы можете развернуть созданное приложение как отдельный микросервис, который:

  • Периодически выгружает новые данные.
  • Сохраняет их в SQL/BLOB-хранилище.
  • Синхронизирует с 1С, ERP, BI-платформой.

Если используется 1С, возможна интеграция через REST API или через экспорт/импорт CSV/XML, данных, полученных через .NET-приложение. Тогда на 1С не ложится сложная задача, а решается более подходящими средствами.

5. Отладка и логирование

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

6. Автоматизация и анализ

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

🧩 7. Как это выглядит на практике

Ниже – примеры из нашей собственной системы:

  • Интерфейс просмотра загруженных отчётов
  • Интерфейс управления выгрузками и анализом данных из МДЛП.

Система используется в продакшене у фармкомпании (Все данные обезличены. Полное демо по запросу.)

  • Архитектура интеграции с 1С и BI-платформами.

Предусмотрены выгрузки для CRM и загрузка оргструктуры и посещений из внешней системы.

Итог

Для быстрого старта рекомендую пройти по шагам из этой статьи, использовать готовые примеры отсюда и с GitHub. Далее нужно будет проработать стратегию интеграции в вашу ERP/CRM-систему. Если задача стоит многоступенчатая – например, построение аналитики – рассмотрите готовые решения и платформы анализа данных из МДЛП, такими как наша BI-система для МДЛП-аналитики (подробнее по запросу).

📩 У вас есть вопросы или задача по МДЛП? Напишите нам – мы поможем с интеграцией и аналитикой.

Форма связи с нами: