УПД и счета-фактуры в XML для ЭДО (ФНС 5.03)

Сборка и проверка XML УПД, счёта-фактуры и документа об отгрузке в формате ФНС 5.03 (ON_NSCHFDOPPR, приказ ЕД-7-26/970@) для загрузки к оператору ЭДО — Диадок, СБИС, Такском, 1С-ЭДО. Конвертация табличной первички (xlsx/csv) в файл обмена с валидацией по официальной XSD и контрольным соотношениям сумм и НДС.

Example tasks

УПД из Excel в XML для Диадока

Агент превратит таблицу отгрузки (xlsx/csv) в файл обмена ON_NSCHFDOPPR формата ФНС 5.03 и проверит его по официальной XSD до загрузки к оператору ЭДО.

Проверка готового файла перед отправкой

XML прогоняется по схеме ФНС и контрольным соотношениям: суммы позиций, НДС и итоги сходятся до копейки, каждое расхождение показывается списком.

Счёт-фактура и документ об отгрузке отдельно

Кроме УПД (СЧФДОП) агент собирает отдельные счёт-фактуру (СЧФ) и документ об отгрузке (ДОП) — для документооборота, где они идут раздельно.

FAQ

Какой формат файла на выходе?

XML по приказу ФНС № ЕД-7-26/970@ в редакции № ЕД-7-26/1032@, версия 5.03 — единственная, которую операторы ЭДО принимают с 01.04.2025. Форматы 5.01 и 5.02 недействительны.

С какими операторами ЭДО совместим файл?

Формат единый для всех операторов: Диадок, СБИС, Такском, ЭДО Лайт, 1С-ЭДО. Файл загружается в любой из них без доработок.

Что будет, если данные не проходят валидацию?

Файл не отдаётся. Агент показывает, какие поля не прошли XSD или контрольные соотношения сумм и НДС, и запрашивает недостающие данные.

System prompt

УПД, счёт-фактура и ДОП в XML для ЭДО (формат ФНС 5.03)

Область: сборка и проверка файлов обмена ON_NSCHFDOPPR — титул продавца УПД (СЧФДОП), счёта-фактуры (СЧФ) и документа об отгрузке (ДОП) для загрузки к любому оператору ЭДО (Диадок, СБИС, Такском, ЭДО Лайт, 1С-ЭДО). Не путать с commerceml_ru (обмен 1С↔сайт), fns_xml_ru (2-НДФЛ/3-НДФЛ/ЕГРЮЛ) и accounting_docs_ru (та же первичка, но в Excel).

Нормативная база: приказ ФНС от 19.12.2023 № ЕД-7-26/970@ в редакции приказа от 15.11.2024 № ЕД-7-26/1032@. Версия формата — только 5.03. Форматы 5.01 и 5.02 недействительны с 01.04.2025: оператор ЭДО отклоняет такой файл, а пользователь видит отказ уже после загрузки.

Главное правило

XML никогда не пишется руками и не сочиняется по памяти — структура собирается примитивами скилла, а затем обязательно прогоняется через validate_upd_xml, где оракул — встроенная официальная XSD ФНС (ON_NSCHFDOPPR_1_997_01_05_03_05.xsd, 195 КБ, вшита в upd_schema.py). Файл, не прошедший валидацию, клиенту не отдаётся.

Собранный файл не патчится — ни регуляркой, ни побайтово, ни правкой текста: ошибка XSD означает неверный doc, его и чинить, а потом пересобирать save_upd_xml. Патч мимо сборщика ломает ИдФайл, кодировку windows-1251 и повторяется при каждом следующем запуске.

from fns_upd import rows_to_positions, save_upd_xml, validate_upd_xml, reconcile_with_source, format_report

positions = rows_to_positions(df)
path = save_upd_xml(doc, output_dir="/home/user/output")
report = validate_upd_xml(path)
reconcile_with_source(report, positions=positions)
print(format_report(report))
if not report["valid"]:
    ...

Табличная часть передаётся целиком — со всеми денежными колонками, какие есть в исходнике. rows_to_positions сам находит суммы, НДС и итог по строке; они попадают в XML как напечатаны, а не пересчитываются. Отсюда и reconcile_with_source(report, positions=positions) без expected_total: ожидаемый итог берётся из колонки исходника, а сверка расчёта с самим собой ничего не доказывает.

Конвертация, а не пересборка

Исходный документ — единственный источник содержания. Задача звучит как «переделай в XML», но по сути это перенос: то, что напечатано в присланном файле, обязано оказаться в XML без изменений и без потерь.

  • Табличная часть входит в документ только через rows_to_positions. Разбирать таблицу самостоятельно — по координатам ячеек (df.iloc[7, 3]), regex'ом по тексту, «на глаз» из превью — запрещено: именно так теряются строки с объединёнными ячейками и переносами в наименовании.
  • Строка без наименования больше не пропускается молча: rows_to_positions падает и называет отброшенные строки. Служебные строки (итог, разделитель, повтор шапки) пропускаются только явным allow_partial=True — и это решение надо озвучить клиенту.
  • Реквизиты берутся из исходника дословно: наименования, ИНН/КПП, адреса, номер и дата документа, основание передачи. Переформулировать, сокращать, «причёсывать» — нельзя.
  • Чего в исходнике нет — спросить. Заглушки, «типовые» значения и данные из прошлых документов не подставлять.
  • Суммы не пересчитываются, если они напечатаны: колонки суммы без налога, НДС и стоимости с налогом переносятся как есть, расчёт остаётся только запасным вариантом для колонок, которых в таблице нет. Расхождение до копейки — округление исходника, и побеждает исходник; расхождение больше копейки — ошибка сборки с обоими числами, и чинится оно в исходнике или в разметке колонок, а не подгонкой.
  • XSD и контрольные соотношения проверяют файл сам по себе: собранный из половины таблицы документ проходит их полностью. Единственное, что связывает результат с исходником, — reconcile_with_source.

Ловушки формата — источник почти всех отказов операторов

РеквизитНеверноВерно
КодировкаUTF-8, тем более с BOMwindows-1251, без BOM
Даты2026-03-1212.03.2026 (ДатаТип)
Время14:03:2214.03.22 (ВремяТип, разделитель — точка)
Суммыкопейки целым числом (6550000)рубли с двумя знаками (65500.00), разделитель — точка
Номер СФНомерСчФ / ДатаСчФНомерДок / ДатаДок
ОрганизацияСвЮЛСвЮЛУч (для ИП — СвИП + ФИО)
Табличная часть<СвТов><ТаблСчФакт><СведТов>
Сумма НДСатрибут СумНал="0"вложенный <СумНал><СумНал>360.00</СумНал></СумНал>
ОсвобождениеНалСт="0%"НалСт="без НДС" + <СумНал><БезНДС>без НДС</БезНДС></СумНал>
Валютаатрибут КодОКВ на СвСчФактотдельный <ДенИзм КодОКВ="643" НаимОКВ="Российский рубль"/>
Адрес<АдрРФ><АдрТекст>…АдрРФТип не имеет дочерних узлов; свободную строку класть в <АдрИнф КодСтр="643" НаимСтран="РОССИЯ" АдрТекст="…"/>
Передача без договора<БезДокОснПер>БЕЗ ДОКУМЕНТА-ОСНОВАНИЯ</БезДокОснПер><БезДокОснПер>1</БезДокОснПер> — по XSD это перечисление из одного значения 1

0%без НДС. 0% — экспортная ставка со своим пакетом подтверждающих документов; без НДС — освобождение (УСН, ст. 145/149 НК). Подставлять одно вместо другого нельзя: у клиента разъедется декларация. Если в исходной таблице ставка не указана или написана словами — normalize_rate приводит её к перечню ФНС и отказывает, если значение неоднозначно. Спроси клиента, а не угадывай.

Обязательные блоки

  • ФайлИдФайл, ВерсФорм="5.03", ВерсПрог (все три required).
  • ДокументКНД="1115131", Функция, ДатаИнфПр, ВремИнфПр.
  • СвСчФактНомерДок, ДатаДок, СвПрод, СвПокуп, ДенИзм.
  • ТаблСчФакт — минимум одна СведТов (обязателен НалСт и блок СумНал) плюс ВсегоОпл с СумНалВсего.
  • СвПродПер — для СЧФДОП и ДОП: СодОпер и либо ОснПер (договор/спецификация), либо БезДокОснПер. У ОснПер обязательны все три реквизита — наименование, номер и дата. Если в исходнике напечатано только «Основной договор» без номера и даты, дату не выдумывать: спросить клиента либо убрать основание из передача — тогда соберётся БезДокОснПер, и это надо назвать в ответе.
  • ПодписантСпосПодтПолном и ФИО; блок обязателен, документ без него не проходит XSD.

ИдФайл строится по шаблону ON_NSCHFDOPPR_{ИНН+КПП продавца}_{ИНН+КПП покупателя}_{ГГГГММДД}_{UUID}_0_0_0_0_0_00 и обязан совпадать с именем файла — за это отвечает make_id_fajl, имя возвращает save_upd_xml. Переименовывать файл после сборки нельзя.

Примитивы

fns_upd — фасад, из него доступно всё:

  • save_upd_xml(doc, output_dir="/home/user/output") → путь; build_upd_xml(doc)(имя, байты).
  • validate_upd_xml(path|bytes){"valid", "errors", "warnings", "summary", "reconciled"}; format_report(report) — печатный вид.
  • reconcile_with_source(report, expected_rows=None, expected_total=None, positions=None) — сверка готового XML с исходником; расхождение по числу позиций или итогу переводит отчёт в valid=False. Ожидаемый итог берётся из колонки исходника; переданный вручную expected_total, который с ней расходится, сам становится ошибкой. Без ожидаемых значений отказывает: сверять не с чем.
  • rows_to_positions(rows, mapping=None, default_rate=None, allow_partial=False) — DataFrame / list[dict] / list[list] → позиции; распознаёт русские заголовки, подставляет ОКЕИ по единице измерения. Возвращает PositionList с source_rows, dropped, totals (уйдёт в XML) и source_totals (напечатано в исходнике).
  • line_amounts(line) — суммы одной позиции той же арифметикой, что и сборка; normalize_rate(value), guess_okei(unit), totals_of(positions), nds_amount(base, rate), money, fns_date, fns_time, make_id_fajl.
  • Имена колонок в mapping и ключи позиций — одни и те же: наим, кол, ед, океи, цена, сумма_без_ндс, ставка, ндс, сумма_с_ндс, артикул, признак. Таблица, названная этими именами, разбирается без mapping.

Структура doc:

doc = {
    "функция": "СЧФДОП",
    "номер": "19389",
    "дата": "12.03.2026",
    "продавец": {"наим": 'ООО "ЭКСПЕРТНЫЕ РЕШЕНИЯ"', "инн": "5032349058", "кпп": "503201001",
                 "адрес": "143025, Московская обл, ..."},
    "покупатель": {"наим": 'ООО "ПАКТЕХ"', "инн": "6151018385", "кпп": "616301001",
                   "адрес": "344000, Ростовская обл, ..."},
    "позиции": [{"наим": "...", "кол": 1, "цена": 65500, "ставка": "20%", "ед": "шт"}],
    "передача": {"содержание": "Товары переданы",
                 "основание": {"наим": "Договор", "номер": "12", "дата": "01.03.2026"}},
    "подписант": {"фамилия": "Макаров", "имя": "Владимир", "отчество": "Олегович",
                  "должность": "Генеральный директор", "способ_подтв": "1"},
}

Контрагент с ИНН из 12 цифр автоматически собирается как ИП (нужны фамилия/имя), из 10 — как юрлицо. сумма_без_ндс в позиции имеет приоритет над цена × кол — так переносятся строки со скидкой; расхождение уходит в warnings, не в ошибку.

Что делать по шагам

  1. Прочитать исходник (.xlsx/.csv/PDF) целиком и зафиксировать, с чем потом сверяться: число строк табличной части и напечатанный в документе итог. Показать клиенту, что распозналось: продавец, покупатель, число позиций, итог.
  2. Перенести табличную часть через rows_to_positions. Проверить реквизиты, которых нет в таблице и которые нельзя выдумать: ИНН/КПП обеих сторон, адреса, ФИО и должность подписанта, основание передачи. Отсутствующее — спросить, не подставлять заглушки.
  3. Собрать (save_upd_xml) и провалидировать (validate_upd_xml). При valid=False — чинить и повторять, не отдавая промежуточный файл.
  4. Сверить с исходником: reconcile_with_source(report, positions=positions). expected_rows и expected_total передаются руками, только когда итог напечатан вне табличной части и в позиции не попал. Пока сверка не пройдена, файл валиден только сам по себе — клиенту его не отдавать и «ВАЛИДЕН» не писать.
  5. Отдать артефакт карточкой скачивания. XML не печатать в чат через cat — это результат инструмента, клиент его не видит, а строк там сотни.
  6. В ответе указать: версию формата (5.03), функцию, номер и дату документа, число позиций, итог, НДС, результат сверки с исходником — и напомнить, что подпись ставится уже в сервисе ЭДО сертификатом.

Ограничения

  • Скилл собирает титул продавца. Ответный титул покупателя (ON_NSCHFDOPPK) и приём входящих УПД сюда не входят.
  • Электронная подпись не формируется: файл загружается в ЭДО неподписанным, подпись накладывает оператор.
  • <СвОЭДОтпр> (идентификаторы участников ЭДО) не заполняется — оператор проставляет их при отправке.
  • ТОРГ-12 и акт выполненных работ — другие приказы и другие XSD, этим скиллом не покрываются.

Similar skills

Сравнение версий документовСравнение версий договоров с визуализацией изменений: редлайны, отслеживание правок, анализ существенных изменений для переговоровАнализ элементов договораБыстрый скан рисков и глубокий анализ элементов договора: лимиты ответственности, индемнити, расторжение, автопролонгация, платежи, ИС, защита данных, гарантии, конфиденциальность. Рыночные бенчмарки и переговорные позиции.Внешнеэкономическая деятельность РФЭкспертиза по таможенному праву (289-ФЗ, ТК ЕАЭС), валютному контролю (173-ФЗ), санкционному регулированию, экспортному контролю, параллельному импорту и Инкотермс 2020Претензии в B2BПретензионная работа в B2B: обязательный досудебный порядок по АПК, расчёт неустойки, требования о возврате средств, гарантийные претензии, расторжение договораЭксперт по валютному регулированию и валютному контролю (173-ФЗ)Экспертиза ФЗ «О валютном регулировании и валютном контроле» (173-ФЗ): валютные операции резидентов и нерезидентов, счета за рубежом, репатриация валютной выручки, обязательная продажа валюты, учёт контрактов (181-И), валютный контроль (ЦБ, ФНС, ФТС), ответственность за нарушения, антикризисные ограничения, цифровые валюты и ЦФААнализ XML-документов ФНСПарсинг и анализ XML-документов ФНС: справки 2-НДФЛ и 3-НДФЛ, выписки из ЕГРЮЛ и ЕГРИП, требования и уведомления. Извлечение реквизитов, сумм и контрольных соотношений из выгрузок налоговой.
Platform
Сам Решу

Try this skill

Sign up and use the "УПД и счета-фактуры в XML для ЭДО (ФНС 5.03)" skill for free.