Выгрузить данные из МДЛП: авторизация, интеграция 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-система для МДЛП-аналитики (подробнее по запросу).
📩 У вас есть вопросы или задача по МДЛП? Напишите нам – мы поможем с интеграцией и аналитикой.
Форма связи с нами: