Работа с электронным документооборотом (ЭДО) в устаревших или самописных конфигурациях на платформе 1С 7.7 сопряжена с рядом технических сложностей: от отсутствия штатных средств формирования HTTP-запросов до нюансов работы с криптографией. В данной статье мы подробно разберем, как реализовать автоматическое подписание входящих универсальных передаточных документов (УПД) через API «ЭДО Лайт» (сервис от «Честного знака»), включая получение подписи отправителя и формирование корректного multipart/form-data запроса.
Прежде чем приступать к написанию кода, проанализируем регламент взаимодействия. По регламенту ФНС, после получения входящего УПД покупатель должен сформировать и отправить в ответ «Титул покупателя» (техническое название: информация покупателя). Для этого необходимо:
doc_id документа, сам XML титула и свою подпись в формате Base64.Одной из главных проблем при работе с API ЭДО Лайт является поиск открепленной подписи отправителя. Рассмотрим наиболее надежные способы ее получения без ручного скачивания из личного кабинета:
GET /api/v1/incoming-documents/{doc_id}/events. В списке событий необходимо найти первичный входящий файл. Поле signature в объекте события содержит искомую строку подписи в формате Base64.GET /api/v1/incoming-documents/{doc_id}/events/{event_id}/content, указав ID события типа 112003.GET /api/v1/incoming-documents/{doc_id}/content (с заголовком Accept: application/zip) возвращает архив. Из него можно извлечь файл с расширением .p7s, который является бинарной подписью. Для передачи через API этот файл необходимо перевести в строку Base64.Для подписания файлов в 1С 7.7 рекомендуется использовать объект CAdESCOM.CadesSignedData (требуется установленный КриптоПро CSP и плагин). Проанализируем пример функции на языке JScript, вызываемой из 1С, которая формирует открепленную подпись в Base64:
// Скрипт для формирования открепленной подписи через CADESCOM
СтрКода = "function SignFile(FileName, Cert, OutFileName) {
| var InStream = new ActiveXObject('ADODB.Stream');
| InStream.Type = 1; // binary
| InStream.Open();
| InStream.LoadFromFile(FileName);
| var InData = InStream.Read(-1);
|
| var Signer = new ActiveXObject('CAdESCOM.CpSigner');
| Signer.Certificate = Cert;
| Signer.Options = 2; // CAPICOM_CERTIFICATE_INCLUDE_END_ENTITY_ONLY
|
| var SignedData = new ActiveXObject('CAdESCOM.CadesSignedData');
| SignedData.Content = InData;
|
| // Параметр 2: 1 - открепленная подпись, Параметр 3: 0 - кодировка Base64
| var OutSignedData = SignedData.SignCades(Signer, 1, 1, 0);
|
| var OutStream = new ActiveXObject('ADODB.Stream');
| OutStream.Type = 2; // text
| OutStream.CharSet = 'US-ASCII';
| OutStream.Open();
| OutStream.WriteText(OutSignedData);
| OutStream.SaveToFile(OutFileName, 2);
| OutStream.Close();
| return 0;
|}";
Метод отправки титула требует передачи данных в формате multipart/form-data. Платформа 1С 7.7 не имеет встроенных средств для комфортной работы с этим типом запросов (объекты WinHttp часто искажают бинарные данные). Оптимальное решение — использование внешней компоненты curl1c (известной как «Карлик»).
Рассмотрим пример формирования запроса на подписание:
Процедура ОтправитьТитул(ПутьКТитулу, ИдДокумента, ПутьКПодписи)
Карлик = СоздатьОбъект("Карлик");
Карлик.УстановитьПараметр("URL", "https://edo-gismt.crpt.ru/api/v1/incoming-documents/xml/upd/title");
Карлик.УстановитьПараметр("CUSTOMREQUEST", "POST");
// Установка авторизации (токен должен быть получен заранее)
Карлик.УстановитьПараметр("HTTPHEADER", "Authorization: Bearer " + ТекТокен);
Карлик.УстановитьПараметр("HTTPHEADER", "Content-Type: multipart/form-data");
Запрос = Карлик.СоздатьЗапрос();
// Читаем содержимое файлов в строки
ТекстТитула = ЧитатьФайлВСтроку(ПутьКТитулу, "windows-1251");
ТекстПодписи = ЧитатьФайлВСтроку(ПутьКПодписи, "ascii");
// Добавляем части multipart-запроса
// Параметры: ИмяПоля, ИмяФайла, ТипКонтента, Данные
Запрос.Добавить("content", "title.xml", "application/xml", ТекстТитула);
Запрос.Добавить("doc_id", , "text/plain", ИдДокумента);
Запрос.Добавить("signature", , "text/plain", ТекстПодписи);
КодОшибки = Карлик.Выполнить();
Если КодОшибки <> 200 Тогда
Сообщить("Ошибка API: " + Карлик.Приемник.Данные);
Иначе
Сообщить("Титул успешно отправлен");
КонецЕсли;
КонецПроцедуры
Выясним причину этой ошибки. Она возникает, когда XML-файл титула передается на сервер в кодировке UTF-8, хотя в заголовке XML указано windows-1251, или наоборот. Сервер Честного знака очень чувствителен к этому.
Решение: Убедитесь, что при чтении XML-файла из 1С и передаче его в объект запроса сохраняется оригинальная кодировка windows-1251. Не используйте парсеры, которые автоматически перекодируют данные в UTF-8 при сохранении. Если вы используете ADODB.Stream, всегда явно указывайте CharSet = 'windows-1251'.
Проанализируем ситуацию, когда при выполнении запроса платформа 1С 7.7 аварийно завершает работу (Runtime Error). Часто это связано с одновременным использованием объекта MSXML2.DOMDocument для парсинга и внешней компоненты для HTTP-запросов в одной процедуре. Компоненты могут конфликтовать в адресном пространстве памяти.
Решение: Попробуйте заменить MSXML DOM на использование анализатора XML из библиотеки v7plus.dll для разбора входящих данных. Если же вам необходимо использовать MSXML, старайтесь уничтожать объект (присваивать 0) перед вызовом методов отправки запроса.
Важно помнить, что кроме Титула покупателя, регламент ЭДО требует отправки «Извещения о получении». В ЭДО Лайт при первом вызове метода получения контента УПД через API, сервер автоматически создает неподписанное событие извещения. Его также нужно найти в списке /unsigned-events, подписать и отправить обратно, иначе документооборот не будет считаться завершенным (статус останется «Просмотрен»).
Для извлечения подписей и XML из архива документооборота потребуется работа с ZIP. Поскольку в 7.7 нет встроенного объекта ZipArchive, можно использовать:
DialMail.dll (объект AddIn.ZIP).BinaryData в библиотеке 1С++ (методы pkUnzip).КомандаСистемы (например, 7-zip).Пример использования DialMail для извлечения файлов по маске:
ZIP = СоздатьОбъект("AddIn.ZIP");
ZIP.КаталогИзвлечения = "C:\Temp\";
// Извлекаем файлы, содержащие GUID документа в имени
КолФайлов = ZIP.Извлечь( , "*" + GUID + "*.*", ПутьКАрхиву);
Таким образом, комбинируя современные средства криптографии, внешние компоненты для работы с сетью и классические приемы программирования в 1С 7.7, можно построить полноценную и надежную систему работы с ЭДО Лайт, не уступающую возможностям современных конфигураций на платформе 8.3.