Почти все, кто имеет ЭДО Диадок в Директуме, наверное, сталкивались с тем, что иногда поступают такие документы, в которых ответственный за контрагента ничего не может понять в открывшемся xml-тексте. Приходится обращаться к ответственному за кабинет оператора, чтобы попробовать извлечь pdf-представление.
Данная статья помогает автоматизировать бОльшую часть процесса загрузки печатных представлений и ускорить документооборот.
Как я уже упомянул, речь идет об операторе Контур. Механизм основан на использовании API Diadoc и соответствующей внешней библиотеке. Полагаю, аналогично можно все сделать и у других операторов, если они имеют API.
Итак начнем.
// URL веб-сервиса Диадок
public const string DefaultApiUrl = "https://diadoc-api.kontur.ru";
/// <summary>
/// Загружает из Диадока входящий документ в печатном представлении pdf и сохраняет его в PublicBody документа
/// </summary>
[Public, Remote]
public bool DownloadDocDiadocAsPdf(Sungero.Docflow.IOfficialDocument document)
{
// Активен ли НОР
var nor = document.BusinessUnit;
if (! nor.Status.Equals(Sungero.Company.BusinessUnit.Status.Active))
return false;
// Находим соотвтетсвующий активный абонентский ящик Директума
var box = OurSolution.OurOrg.BusinessUnitBoxes.GetAll(r => r.Status.Equals(Sungero.ExchangeCore.BusinessUnitBox.Status.Active)&& r.BusinessUnit.Equals(nor)).FirstOrDefault();
if (box == null)
return false;
// Идентификатор клиента, он же апи-ключ разработчика
var apiKey = box.API;
if (apiKey == null)
return false;
// Определяем ссылку на нужный документ у оператора обмена
var docInfo = Sungero.Exchange.PublicFunctions.ExchangeDocumentInfo.Remote.GetLastDocumentInfo(document);
// Определяем расширение файла и обрабатываем желаемые
var version = document.LastVersion;
string extension = document.LastVersion.AssociatedApplication.Extension;
string[] listExt = {"_", "xml", "*"};
if (docInfo == null
|| string.IsNullOrEmpty(extension)
|| ! listExt.Contains(extension))
return false;
try
{
var crypt = new WinApiCrypt();
var diadocApi = new DiadocApi(apiKey, Constants.Module.DefaultApiUrl, crypt);
// Запрашиваем сеансовый токен в Диадоке
var authTokenByLogin = diadocApi.Authenticate(box.Login, Constants.Module.DefaultPassword);
// Определяем ИД ящика Диадока для НОР
var foundOrgs = diadocApi.GetOrganizationsByInnKpp(nor.TIN, nor.TRRC).Organizations;
var foundOrg = foundOrgs.FirstOrDefault(r => ! r.IsRoaming);
var boxId = foundOrg.Boxes.FirstOrDefault().BoxIdGuid;
int sleep = 1;
byte[] bytes = null;
// В цикле ожидаем готовности запрошенного документа
while (sleep > 0)
{
var result = diadocApi.GeneratePrintForm(authTokenByLogin, boxId, docInfo.ServiceMessageId, docInfo.ServiceDocumentId);
sleep = result.RetryAfter;
if (sleep >0)
System.Threading.Thread.Sleep(sleep);
if (result.HasContent)
bytes = result.Content.Bytes;
};
// Если надо, то сохраняем результат в файл
System.IO.File.WriteAllBytes("C:\\temp\\DiadocFile_" + Calendar.Now.ToString("yyMMdd_HHmmss") + ".pdf", bytes);
// Сохраняем результат в видимое представление документа
using (var memoryStream = new System.IO.MemoryStream(bytes))
{
version.PublicBody.Write(memoryStream);
version.AssociatedApplication = Sungero.Content.AssociatedApplications.GetByExtension("pdf");
document.Save();
}
return true;
}
catch (Exception ex)
{
string msg = ex.Message;
}
return false;
}
public virtual void Script4InnerAuditExecute()
{
var atts = _obj.Attachments;
foreach (var att in atts)
if (Sungero.Docflow.OfficialDocuments.Is(att))
{
var document = Sungero.Docflow.OfficialDocuments.As(att);
if (document.DocumentKind.Code == 'ВХЭДО')
bool b = OurSolution.InnAudit.PublicFunctions.Module.Remote.DownloadDocDiadocAsPdf(document);
}
}
Знаю, что многие запрашивали реализацию аналогичной фичи давно и в Клубе, и у техподдержки. Директум обещал, но пока не реализовал. Мы же ждать больше не можем, т.к. поток документов ЭДО удваивается каждый год.
Берите, пользуйтесь !
Также замечу, что это описание несет гораздо больше, чем просто про xml, т.к. оно может подтолкнуть вас в "ворота к безграничной власти" над ящиками оператора ЭДО. Это полное управление вами из DRX множеством операций и по своей логике - отправкой/приемом документов, считывание дополнительных значений, управление сертификатами, и т.д. и т.п.!
Исключение: Если xml приходит от контрагента, который не в Диадоке, а от другого оператора, то, скорее всего, даже Диадок покажет его вам вот таким образом и тут уже ничто не поможет. Поможет только расширение федералами списка формализованных типов документов по ФЗ-63.
С исключениями также можно бороться если заморочиться и запросить у техподдержек разных операторов их форматы документов, они могут предоставить (а могут и не предоставить) xsd-схемы с описанием документов. По ним можно парсить и внешние относительно контура документы. Придется написать много кода, а также много хардкода, но это сработает.
Спасибо за статью.
Очень жаль, что коннектор не реализует весь функционал, доступный в апи, иногда это очень требуется.
Антон, вот-вот .
1. А зачем, если есть более быстрый и легкий путь получения того же результата?
2. И еще, представьте, что у вас несколько операторов и у каждого надо запрашивать схемы всех типов доков(!) и под каждого опять программировать!.. Но у всех есть простая апи-функция визуального представления - это единственный оператор, которым воспользоваться проще для добавления и последующего текущего сопровождения.
"Следовательно, правильным будет сделать еще одно свойство и контрол в BusinessUnitBox для поля пароля, т.к. уже заведенный вами пароль в штатном базовом поле при установлении соединения ящика хранится в зашифрованном виде."
Вот так можно сделать:
Sungero.Core.Encryption.Decrypt(box.Password)
Еще есть нюанс, если печатную форму создавать не во время получения документа из сервиса обмена, а позже, например старые документы, созданные до данной доработки. Теоретически НОР в документе может быть изменена, поэтому лучше её получить следующим образом:
var nor = docInfo.RootBox?.BusinessUnit;
Однако нужно быть аккуратным, в старых версиях был баг и RootBox был равен Box'у подразделения
Авторизуйтесь, чтобы написать комментарий