При интеграции платформы 1С:Предприятие со сложными внешними SOAP/WCF веб-сервисами разработчики часто сталкиваются с двумя серьезными препятствиями: специфической авторизацией уровня сообщения (например, TransportWithMessageCredential с токенами WS-Security UserNameToken) и некорректной передачей дат, когда день и месяц самопроизвольно меняются местами или вызывают ошибку при значениях дня больше 12.
Разберем детально, почему возникают эти ошибки, как правильно настроить передачу параметров даты через .NET-мост и какие существуют альтернативные способы прямого взаимодействия из 1С без использования внешних библиотек.
Выясним причину, по которой дата 01.12.2017 превращается в 12.01.2017, а запросы с датой после 12-го числа месяца завершаются ошибкой:
Дата из 1С в управляемый код .NET через COM-маршалинг значение может преобразовываться в строку. Если вызывающий процесс или поток .NET инициализирован с культурой en-US или InvariantCulture, строковое представление даты формата дд.мм.гггг интерпретируется по американскому стандарту ММ/дд/гггг. Если день равен, например, 15, парсер не может распознать 15-й месяц и выдает ошибку.System.DateTime приводит к сбоям сериализации SOAP-пакета.WSСсылки и WSПрокси в 1С не поддерживают привязку TransportWithMessageCredential (передача логина и пароля в заголовке wsse:Security по защищенному каналу HTTPS), из-за чего разработчикам приходится использовать внешние прокси-компоненты или формировать запросы вручную.Если интеграция реализована через вспомогательную библиотеку (например, NetObjectToIDispatch или собственную COM-видимую сборку C#), необходимо исключить неявное преобразование дат в строки. Создадим объект System.DateTime явно, передав числовые составляющие даты (год, месяц, день, часы, минуты, секунды).
Посмотрим на пример правильного формирования даты в 1С:
// Получаем доступ к типу DateTime в среде CLR
ТипДатаВремя = Врап.ПолучитьТип("System.DateTime");
// Создаем объект строго по числовым параметрам (Год, Месяц, День, Час, Минута, Секунда)
ИсходнаяДата = ТекущаяДата();
ДатаДляСервиса = Врап.СоздатьОбъект(ТипДатаВремя,
Год(ИсходнаяДата),
Месяц(ИсходнаяДата),
День(ИсходнаяДата),
Час(ИсходнаяДата),
Минута(ИсходнаяДата),
Секунда(ИсходнаяДата));
// Передаем строго типизированную дату в метод веб-сервиса
Результат = КлиентСервиса.GetInvoices(ДатаДляСервиса);
Также можно воспользоваться методом принудительного приведения типов ChangeType:
Дата1 = Врап.ChangeType("System.DateTime", ТекущаяДата());
Важно: Передача даты отдельными числовыми компонентами полностью исключает влияние локали потока (CultureInfo) и гарантирует правильную сериализацию дня и месяца.
Если вы пишете собственную библиотеку-адаптер на C# для вызова WCF-сервиса, проанализируем, как правильно принимать строковые параметры из 1С. Избегайте использования стандартного DateTime.Parse без указания формата.
Рассмотрим пример безопасного парсинга в коде C#:
public InvoiceResult[] GetInvoicesByDateString(string dateString)
{
// Строго задаем формат и культуру для исключения путаницы дней и месяцев
DateTime parsedDate;
string[] formats = { "yyyy-MM-ddTHH:mm:ss", "yyyy-MM-dd", "dd.MM.yyyy HH:mm:ss", "dd.MM.yyyy" };
if (!DateTime.TryParseExact(dateString, formats, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out parsedDate))
{
throw new ArgumentException("Неверный формат даты: " + dateString);
}
return _client.GetInvoices(parsedDate);
}
Поскольку встроенный WSПрокси не поддерживает заголовок безопасности TransportWithMessageCredential, наиболее надежным и кроссплатформенным решением для 1С является формирование сырого SOAP-запроса через стандартный объект HTTPСоединение.
Разберем по шагам реализацию этого подхода:
wsse:Security.XMLСтрока.HTTPЗапрос.Рассмотрим программную реализацию:
Сервер = "api-efactura.fisc.md";
Ресурс = "/Service.svc";
// Формируем дату по стандарту XML (yyyy-MM-ddTHH:mm:ss)
ДатаЗапроса = XMLСтрока(ТекущаяДата());
// Формируем тело SOAP-конверта с авторизацией WS-Security
ТекстЗапроса =
"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:tem=""http://tempuri.org/"" xmlns:oas=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"">" +
" <soapenv:Header>" +
" <oas:Security>" +
" <oas:UsernameToken>" +
" <oas:Username>supplier</oas:Username>" +
" <oas:Password>supplier</oas:Password>" +
" </oas:UsernameToken>" +
" </oas:Security>" +
" </soapenv:Header>" +
" <soapenv:Body>" +
" <tem:GetInvoices>" +
" <tem:date>" + ДатаЗапроса + "</tem:date>" +
" </tem:GetInvoices>" +
" </soapenv:Body>" +
"</soapenv:Envelope>";
SSL = Новый ЗащищенноеСоединениеOpenSSL();
HTTPСоединение = Новый HTTPСоединение(Сервер, 443, , , , 30, SSL);
HTTPЗапрос = Новый HTTPЗапрос(Ресурс);
HTTPЗапрос.Заголовки.Вставить("Content-Type", "text/xml; charset=utf-8");
HTTPЗапрос.Заголовки.Вставить("SOAPAction", "http://tempuri.org/IService/GetInvoices");
HTTPЗапрос.УстановитьТелоИзСтроки(ТекстЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Если HTTPОтвет.КодСостояния = 200 Тогда
ОтветXML = HTTPОтвет.ПолучитьТелоКакСтроку();
// Обработка полученного XML через ЧтениеXML или ФабрикуXDTO
Иначе
ВызватьИсключение "Ошибка сервиса: " + HTTPОтвет.КодСостояния + " " + HTTPОтвет.ПолучитьТелоКакСтроку();
КонецЕсли;
Преимущества прямого HTTP-запроса:
XMLСтрока(Дата) всегда формирует канонический формат даты ГГГГ-ММ-ДДTЧЧ:ММ:СС, что исключает ошибки региональных настроек операционной системы.