Разработка интеграции с системами электронного документооборота (ЭДО) для выгрузки первичных документов и счетов-фактур в формате XML — задача, с которой сталкивается каждый разработчик 1С. В процессе реализации часто возникают технические трудности, связанные со строгим форматно-логическим контролем (ФЛК) со стороны ФНС и жесткими требованиями крупных торговых сетей (ритейлеров). В рамках данной статьи мы подробно разберем, как правильно спроектировать выгрузку сведений об адресах, как автоматически определять коды регионов для индивидуальных предпринимателей и как собрать XML-документ наиболее простым и производительным способом без использования тяжелых механизмов DOM или XDTO.
Несмотря на то, что исторически обсуждение опиралось на формат 820@ версии 5.01, описанные архитектурные подходы и алгоритмы разбора данных остаются фундаментальными и полностью применимыми для актуальных форматов электронного документооборота (включая современный формат УПД версии 5.03). Мы рассмотрим практические аспекты реализации этих механизмов как на платформе 1С:Предприятие 8, так и на классической платформе 1С:Предприятие 7.7.
При анализе XSD-схемы формата обмена элемента АдресТип мы сталкиваемся с конструкцией xs:choice, которая объединяет три элемента:
АдрРФ — структурированные сведения об адресе на территории Российской Федерации.АдрИнф — произвольное текстовое представление адреса (включая адреса за пределами РФ).КодГАР — уникальный номер адреса объекта адресации в Государственном адресном реестре (актуальный аналог ФИАС).Спецификация xs:choice накладывает строгое ограничение: в XML-документе должен присутствовать только один из этих трех элементов. Попытка выгрузить одновременно несколько тегов приведет к ошибке ФЛК на этапе отправки файла оператору ЭДО.
Некоторые компании (например, крупные маркетплейсы) часто упрощают выгрузку, заполняя исключительно тег АдрИнф текстовой строкой и указывая код страны (например, КодСтр="643" для РФ). Рассмотрим типичный пример такой упрощенной выгрузки:
<СвПрод>
<ИдСв>
<СвЮЛУч ИННЮЛ="7721546864" НаимОрг="Общество с ограниченной ответственностью 'Вайлдберриз'" КПП="997750001"/>
</ИдСв>
<Адрес>
<АдрИнф КодСтр="643" АдрТекст="142715, Московская обл, Ленинский р-н, Мильково д, владение 1"/>
</Адрес>
</СвПрод>
Несмотря на то, что ФЛК оператора пропускает документы с тегом АдрИнф для российских адресов, использовать этот подход на постоянной основе крайне не рекомендуется. Многие крупные торговые сети (например, X5 Group, Магнит, Лента) требуют исключительно структурированный адрес в формате АдрРФ. Если передать им неструктурированную строку, их внутренние учетные системы не смогут автоматически обработать и сопоставить адрес грузоотправителя или грузополучателя. Это приведет к ручному отклонению документа и задержке оплат.
Поэтому правильным подходом является формирование тега АдрРФ со структурированными дочерними реквизитами:
<Адрес>
<АдрРФ КодРегион="77" Индекс="103473" Город="Москва г" Улица="Суворовская пл-дь" Дом="1"/>
</Адрес>
При заполнении тега АдрРФ возникает задача корректного переноса адресных сведений из базы 1С. В типовых конфигурациях 1С адрес хранится в виде структурированной информации КЛАДР или ФИАС, однако состав полей в XML-схеме ФНС может отличаться. Проанализируем оптимальную схему мэппинга полей при выгрузке:
Дом.
Корпус.
Кварт.
Рассмотрим пример кода на языке 1С для сопоставления структуры адреса перед выводом в XML:
// Пример сопоставления полей адреса для выгрузки в XML
Функция СформироватьПараметрыАдресаРФ(СтруктураАдреса1С) Экспорт
ПараметрыАдреса = Новый Структура;
ПараметрыАдреса.Вставить("КодРегион", СтруктураАдреса1С.КодРегиона);
ПараметрыАдреса.Вставить("Индекс", СтруктураАдреса1С.Индекс);
ПараметрыАдреса.Вставить("Район", СтруктураАдреса1С.Район);
ПараметрыАдреса.Вставить("Город", СтруктураАдреса1С.Город);
ПараметрыАдреса.Вставить("НаселПункт", СтруктураАдреса1С.НаселенныйПункт);
ПараметрыАдреса.Вставить("Улица", СтруктураАдреса1С.Улица);
// Объединяем специфичные поля в реквизит "Дом"
НомерДома = СтруктураАдреса1С.Дом;
Если ЗначениеЗаполнено(СтруктураАдреса1С.Владение) Тогда
НомерДома = НомерДома + " вл " + СтруктураАдреса1С.Владение;
КонецЕсли;
ПараметрыАдреса.Вставить("Дом", СокрЛП(НомерДома));
// Объединяем корпус, строение и участок в реквизит "Корпус"
НомерКорпуса = СтруктураАдреса1С.Корпус;
Если ЗначениеЗаполнено(СтруктураАдреса1С.Строение) Тогда
НомерКорпуса = НомерКорпуса + " стр " + СтруктураАдреса1С.Строение;
КонецЕсли;
Если ЗначениеЗаполнено(СтруктураАдреса1С.Участок) Тогда
НомерКорпуса = НомерКорпуса + " уч " + СтруктураАдреса1С.Участок;
КонецЕсли;
ПараметрыАдреса.Вставить("Корпус", СокрЛП(НомерКорпуса));
// Объединяем квартиру, офис и помещение в реквизит "Кварт"
НомерКвартиры = СтруктураАдреса1С.Квартира;
Если ЗначениеЗаполнено(СтруктураАдреса1С.Офис) Тогда
НомерКвартиры = НомерКвартиры + " оф " + СтруктураАдреса1С.Офис;
КонецЕсли;
Если ЗначениеЗаполнено(СтруктураАдреса1С.Помещение) Тогда
НомерКвартиры = НомерКвартиры + " пом " + СтруктураАдреса1С.Помещение;
КонецЕсли;
ПараметрыАдреса.Вставить("Кварт", СокрЛП(НомерКвартиры));
Возврат ПараметрыАдреса;
КонецФункции
При выгрузке адресов для федеральных городов значения имеют свои особенности. Для Москвы (код региона 77), Санкт-Петербурга (код региона 78) и Севастополя (код региона 92) поле Город в XML-файле может не заполняться, если адрес находится непосредственно внутри границ самого города-субъекта. Однако, если адрес относится к городу-спутнику (например, г. Зеленоград в составе Москвы или г. Колпино в составе Санкт-Петербурга), название этого населенного пункта обязательно должно быть выгружено в атрибут Город.
Согласно требованиям формата, в элементе АдрРФ атрибут КодРегион является строго обязательным. Для юридических лиц его можно относительно легко получить, отрезав первые два символа от КПП контрагента (за исключением крупнейших налогоплательщиков с КПП, начинающимися на «99», для которых код региона нужно определять по юридическому адресу). Но как быть с индивидуальными предпринимателями (ИП), у которых КПП отсутствует в принципе, а адреса в базе зачастую забиты в виде неструктурированной строки?
Для решения этой проблемы спроектируем и разберем пошаговый алгоритм парсинга неструктурированного адреса в 1С:
АдрИнф.
КодРегион.
АдрИнф, чтобы не заблокировать процесс отправки документа.
Рассмотрим пример реализации функции парсинга региона по строке адреса на языке 1С:
// Функция возвращает двузначный код региона РФ по текстовому адресу
Функция ОпределитьКодРегионаПоСтроке(Знач ТекстАдреса, ТаблицаРегионов) Экспорт
ТекстАдреса = ВРег(ТекстАдреса);
// Проверим простейшие вхождения
Для Каждого СтрокаРегиона Из ТаблицаРегионов Цикл
// Ищем точное вхождение названия региона, например "СВЕРДЛОВСКАЯ" или "МОСКВА"
Если Найти(ТекстАдреса, ВРег(СтрокаРегиона.Наименование)) > 0 Тогда
Возврат СтрокаРегиона.Код;
КонецЕсли;
// Проверяем синонимы и сокращения
Если ЗначениеЗаполнено(СтрокаРегиона.Синоним) И Найти(ТекстАдреса, ВРег(СтрокаРегиона.Синоним)) > 0 Тогда
Возврат СтрокаРегиона.Код;
КонецЕсли;
КонецДляОбохода;
// Если регион не найден, возвращаем пустую строку
Возврат "";
КонецФункции
Традиционный путь создания XML-файлов в 1С через объектную модель документов DOM или использование механизмов XDTO-пакетов имеет очевидные недостатки. Он требует написания огромного объема кода для инициализации каждого узла, строгой типизации всех элементов и потребляет значительные ресурсы сервера при обработке больших пакетов документов (что критично для высоконагруженных систем выгрузки и старых платформ типа 1С 7.7).
Проанализируем альтернативный, гораздо более производительный и гибкий метод формирования XML — метод текстовой шаблонизации. Суть метода заключается в разделении структуры XML-документа на несколько логических блоков-шаблонов:
АдрРФ и шаблон для АдрИнф).Шаблоны хранятся в виде текстовых макетов в конфигурации. Внутри шаблонов прописываются специальные маркеры (плейсхолдеры) со знаком процента, например: %FILEID%, %DOCNUM%, %FIRMADDR%. При формировании документа мы просто заполняем структуру данных в 1С и производим быструю замену маркеров на реальные значения с помощью функции СтрЗаменить().
Рассмотрим структуру типового шаблона для выгрузки УПД:
<?xml version="1.0" encoding="windows-1251"?>
<Файл ИдФайл="%FILEID%" ВерсФорм="5.01" ВерсПрог="1C">
<СвУчДокОбор ИдОтпр="%SENDERID%" ИдПол="%RECEIVERID%">
<СвОЭДОтпр НаимОрг="%SENDEROPERNAME%" ИННЮЛ="%SENDEROPERINN%" ИдЭДО="%SENDEROPERID%" />
</СвУчДокОбор>
<Документ КНД="1115125" Функция="СЧФДОП" ПоФактХЖ="Документ об отгрузке товаров" ДатаИнфПр="%FORMDATE%" ВремИнфПр="%FORMTIME%" НаимЭконСубСост="%FIRMNAME%">
<СвСчФакт НомерСчФ="%DOCNUM%" ДатаСчФ="%DOCDATE%" КодОКВ="643">
%CORRECTIONDATA%
<СвПрод>
<ИдСв>
<СвЮЛУч НаимОрг="%FIRMNAME%" ИННЮЛ="%FIRMINN%" КПП="%FIRMKPP%" />
</ИдСв>
<Адрес>
%FIRMADDR%
</Адрес>
</СвПрод>
<ГрузОт>
<ОнЖе>он же</ОнЖе>
</ГрузОт>
<ГрузПолуч>
<ИдСв>
<СвЮЛУч НаимОрг="%CONSNAME%" ИННЮЛ="%CONSINN%" КПП="%CONSKPP%" />
</ИдСв>
<Адрес>
%CONSADDR%
</Адрес>
</ГрузПолуч>
<СвПокуп>
<ИдСв>
<СвЮЛУч НаимОрг="%CONTNAME%" ИННЮЛ="%CONTINN%" КПП="%CONTKPP%" />
</ИдСв>
<Адрес>
%CONTADDR%
</Адрес>
</СвПокуп>
</СвСчФакт>
<ТаблСчФакт>
%GOODSTABLE%
<ВсегоОпл СтТовБезНДСВсего="%TOTALWOVAT%" СтТовУчНалВсего="%TOTALWITHVAT%">
<СумНалВсего>
<СумНал>%TOTALVAT%</СумНал>
</СумНалВсего>
<НеттоВс>%TOTALQTY%</НеттоВс>
</ВсегоОпл>
</ТаблСчФакт>
<Подписант ОблПолн="6" Статус="1" ОснПолн="Должностные обязанности">
<ЮЛ НаимОрг="%FIRMNAME%" ИННЮЛ="%FIRMINN%" Должн="%SIGNEREMPNAME%">
<ФИО Имя="%SIGNERNAME%" Фамилия="%SIGNERFAMILY%" Отчество="%SIGNERSURNAME%" />
</ЮЛ>
</Подписант>
</Документ>
</Файл>
Теперь разберем, как выглядит программный алгоритм в 1С, который собирает готовый файл из этого шаблона. Использование функции СтрЗаменить гарантирует мгновенное выполнение сборки текстового файла даже на слабых рабочих станциях.
Процедура СформироватьXMLДокумент(ДокументСсылка, ПутьВыгрузки)
// Получаем текст основного шаблона из макета
ТекстДокумента = ПолучитьТекстШаблона("МакетШаблонаУПД");
// Заполняем простые реквизиты шапки
ТекстДокумента = СтрЗаменить(ТекстДокумента, "%FILEID%", ПолучитьУникальныйИдентификаторФайла(ДокументСсылка));
ТекстДокумента = СтрЗаменить(ТекстДокумента, "%DOCNUM%", ДокументСсылка.Номер);
ТекстДокумента = СтрЗаменить(ТекстДокумента, "%DOCDATE%", Формат(ДокументСсылка.Дата, "ДФ=гггг-ММ-дд"));
ТекстДокумента = СтрЗаменить(ТекстДокумента, "%FORMDATE%", Формат(ТекущаяДата(), "ДФ=гггг-ММ-дд"));
ТекстДокумента = СтрЗаменить(ТекстДокумента, "%FORMTIME%", Формат(ТекущаяДата(), "ДФ=ЧХ:ММ:СС"));
// Заполняем реквизиты нашей организации (Продавца)
Организация = ДокументСсылка.Организация;
ТекстДокумента = СтрЗаменить(ТекстДокумента, "%FIRMNAME%", Организация.НаименованиеПолное);
ТекстДокумента = СтрЗаменить(ТекстДокумента, "%FIRMINN%", Организация.ИНН);
ТекстДокумента = СтрЗаменить(ТекстДокумента, "%FIRMKPP%", Организация.КПП);
// Формируем блок адреса продавца
ТекстАдресаПродавца = СформироватьXMLБлокАдреса(Организация.ЮридическийАдрес);
ТекстДокумента = СтрЗаменить(ТекстДокумента, "%FIRMADDR%", ТекстАдресаПродавца);
// Формируем табличную часть товаров
ТекстТаблицы = "";
Для Каждого СтрокаТЧ Из ДокументСсылка.Товары Цикл
ШаблонСтроки = ПолучитьТекстШаблона("МакетСтрокиТовара");
ШаблонСтроки = СтрЗаменить(ШаблонСтроки, "%NOMNAME%", СтрокаТЧ.Номенклатура.Наименование);
ШаблонСтроки = СтрЗаменить(ШаблонСтроки, "%QTY%", СтрокаТЧ.Количество);
ШаблонСтроки = СтрЗаменить(ШаблонСтроки, "%PRICE%", СтрокаТЧ.Цена);
ШаблонСтроки = СтрЗаменить(ШаблонСтроки, "%SUM%", СтрокаТЧ.Сумма);
ТекстТаблицы = ТекстТаблицы + ШаблонСтроки + Символы.ПС;
КонецДляОбохода;
ТекстДокумента = СтрЗаменить(ТекстДокумента, "%GOODSTABLE%", ТекстТаблицы);
// Сохраняем готовый файл на диск в кодировке Windows-1251
ЗаписьТекста = Новый ЗаписьТекста(ПутьВыгрузки, КодировкаТекста.ANSI);
ЗаписьТекста.Записать(ТекстДокумента);
ЗаписьТекста.Закрыть();
КонецПроцедуры
Этот подход обладает колоссальной гибкостью. Если у вас появляется контрагент (например, торговая сеть) с уникальными требованиями к информационным полям ИнфПолФХЖ1 (передача GLN номеров, кодов артикулов покупателя, номеров заказов), вам не нужно переписывать сложную логику формирования объектов XML. Достаточно создать отдельный шаблон под этого контрагента, прописать в нем специфические маркеры и заменять их в коде при выгрузке.
Частым вопросом при разработке выгрузки XML является передача сведений о лице, подписывающем документ. Важно понимать, что сам по себе XML-файл, генерируемый в 1С, содержит лишь текстовое описание физического лица в блоке Подписант (его ФИО, должность и полномочия). Сама электронная цифровая подпись (криптографический КЭП) накладывается на файл позже — либо средствами специального крипто-компонента непосредственно внутри 1С при отправке, либо в личном кабинете (ЛК) оператора ЭДО (СБИС, Диадок и др.) после ручной загрузки XML. Для реализации наложения подписи на стороне учетной системы подойдёт обработка для подписания файлов с помощью сертификата ЭЦП средствами 1С.
На практике часто возникает ситуация, когда в теге Подписант указан генеральный директор предприятия, а фактическое подписание файла в ЛК ЭДО производит бухгалтер или менеджер по своей личной КЭП. С технической стороны операторы ЭДО пропускают такие документы, однако с юридической точки зрения это несет серьезные риски. По регламенту ФНС, сведения в XML-файле должны строго соответствовать реквизитам сертификата КЭП, которым подписывается документ.
С развитием законодательства компромиссные схемы, когда сотрудники подписывали документы КЭП директора, стали нелегальными. Современные стандарты ЭДО требуют соблюдения двух правил:
Таким образом, при проектировании структуры 1С для работы с ЭДО необходимо сразу закладывать справочник «Подписанты» с возможностью привязки КЭП конкретных пользователей и хранения GUID их машиночитаемых доверенностей. Это позволит динамически подставлять правильные ФИО и должности в XML-теги и избежать расхождений с реквизитами накладываемых подписей. Для организации ведения и контроля таких сведений можно использовать конфигурацию для учета ЭЦП и МЧД.