BUH_bot/docs/xml_generator_analysis.md
Игорь Надеин a16ffca725 Refactor validator for zero-price detection and fix XML generator for 1C EnterpriseData 1.20 compatibility
- Enhanced validator with critical zero-price validation
- Fixed currency GUIDs and money formatting in XML generator
- Removed incompatible VidED field
- Added comprehensive documentation
- Updated test files
2025-11-06 09:03:35 +03:00

18 KiB
Raw Blame History

Анализ генератора XML EnterpriseData 1.20

Дата анализа: 6 ноября 2025 г.

Проверенные проблемы и их статус

❌ КРИТИЧНО: 1. Жёстко прошитые GUID справочников

Местоположение:

  • Строки 773-776 (валюта RUB)
  • Строки 878, 1027 (страна Россия)
  • Строки 990-994 (единица измерения "шт")

Проблема:

# Валюта - жёстко прошитый GUID
if not currency_id:
    currency_id = "72357d18-63a8-11e8-80dd-0050569f5448"  # RUB

# Страна - жёстко прошитый GUID
etree.SubElement(country_elem, "Ссылка").text = "67700c1b-63a8-11e8-80dd-0050569f5448"

# Единица измерения - жёстко прошитый GUID
if not uom_id:
    uom_id = "cdf130e2-63a8-11e8-80dd-0050569f5448"  # шт (796)

Почему это критично:

  • GUID справочников уникальны для каждой базы 1С
  • В другой базе эти GUID не существуют
  • Импорт завершится ошибкой "Объект не найден"

Рекомендация:

# ПРАВИЛЬНО: Передавать только код и наименование, без Ссылки
# Либо позволить 1С создать объект автоматически
<Валюта>
  <!-- Убрать <Ссылка> если объект не существует -->
  <ДанныеКлассификатора>
    <Код>643</Код>
    <НаименованиеПолное>Российский рубль</НаименованиеПолное>
    <КодЦифровой>643</КодЦифровой>
    <КодСимвольный>RUB</КодСимвольный>
  </ДанныеКлассификатора>
</Валюта>

Решение:

  1. Либо получать GUID из конфигурации целевой базы
  2. Либо использовать флаг "создавать отсутствующие объекты"
  3. Либо убрать <Ссылка> и передавать только классификаторы

⚠️ КРИТИЧНО: 2. Структура валюты/банков не соответствует XSD

Местоположение: Строки 767-780

Проблема:

currency_elem = etree.SubElement(doc, "Валюта")
etree.SubElement(currency_elem, "Ссылка").text = currency_id
currency_data = etree.SubElement(currency_elem, "ДанныеКлассификатора")
etree.SubElement(currency_data, "Код").text = currency.get("isoCode", "643")

Почему это проблема:

  • Структура с вложенным ДанныеКлассификатора может не соответствовать XSD 1.20
  • В типовых схемах ED часто используется плоская структура: <Валюта><Ссылка/><Код/><Наименование/></Валюта>

Рекомендация:

  • ОБЯЗАТЕЛЬНО сверить структуру с XSD принимающей системы
  • Возможно, нужна плоская структура без ДанныеКлассификатора

TODO:

  • Получить актуальный XSD от клиента
  • Проверить версию EnterpriseData в целевой 1С
  • Адаптировать структуру под реальную схему

❌ КРИТИЧНО: 3. Неверные ссылки на банк/счёт

Местоположение: Строки 825-838

Проблема:

bank_acc_elem = etree.SubElement(doc, "БанковскийСчетОрганизации")
acc_id = acc.get("id", "")
etree.SubElement(bank_acc_elem, "Ссылка").text = acc_id  # ID счёта

# Банк
bank_elem = etree.SubElement(bank_acc_elem, "Банк")
etree.SubElement(bank_elem, "Ссылка").text = acc_id  # ❌ ОШИБКА: используем тот же ID

Почему это критично:

  • Для банка должна быть ссылка на объект справочника "Банки" (другой GUID)
  • Для счёта - ссылка на объект справочника "БанковскиеСчета"
  • Использование одного ID для обоих объектов неверно

Рекомендация:

# ПРАВИЛЬНО: Разные ID для банка и счёта
bank_elem = etree.SubElement(bank_acc_elem, "Банк")
bank_id = acc.get("bank_id") or generate_bank_id_by_bic(acc.get("bic"))
etree.SubElement(bank_elem, "Ссылка").text = bank_id

# Или передавать без Ссылки, только классификатор
<Банк>
  <ДанныеКлассификатораБанков>
    <Наименование>Сбербанк</Наименование>
    <БИК>044525225</БИК>
    <КоррСчет>30101810400000000225</КоррСчет>
  </ДанныеКлассификатораБанков>
</Банк>

Решение:

  • Убрать <Ссылка> для банка, передавать только БИК и наименование
  • 1С сама найдёт/создаст банк по БИК

⚠️ СРЕДНЕ: 4. Договор внутри "ДанныеВзаиморасчетов"

Местоположение: Строки 887-922

Проблема:

contract_elem = etree.SubElement(settlement_elem, "Договор")
contract_uuid = str(uuid.uuid5(uuid.NAMESPACE_DNS, f"contract-{cp_id}-{org_id}"))
etree.SubElement(contract_elem, "Ссылка").text = contract_uuid
etree.SubElement(contract_elem, "ВидДоговора").text = "СПокупателем"
# Далее вложенные Организация, Контрагент...

Почему это проблема:

  • Детальная структура договора (с Организацией/Контрагентом внутри) может не соответствовать XSD
  • В ED обычно ссылка на договор - это просто <Ссылка> на существующий объект
  • Полное описание договора должно быть отдельной сущностью в Message

Рекомендация:

<!-- Вариант 1: Только ссылка (если договор уже есть) -->
<Договор>
  <Ссылка>uuid-договора</Ссылка>
</Договор>

<!-- Вариант 2: Полный объект в отдельном блоке Message -->
<Message>
  <Body>
    <Справочник.ДоговорыКонтрагентов>
      <Ссылка>uuid</Ссылка>
      <ВидДоговора>СПокупателем</ВидДоговора>
      <!-- ... -->
    </Справочник.ДоговорыКонтрагентов>
    
    <Документ.РеализацияТоваровУслуг>
      <Договор>
        <Ссылка>uuid</Ссылка> <!-- Ссылка на объект выше -->
      </Договор>
    </Документ.РеализацияТоваровУслуг>
  </Body>
</Message>

TODO:

  • Уточнить у клиента: договоры уже есть в базе или нужно создавать?
  • Если создавать - вынести договор в отдельный блок Message
  • Если использовать существующие - передавать только Ссылка

❌ КРИТИЧНО: 5. Потеря копеек

Местоположение: Строки 1012-1014

Проблема:

price = pos.get("price", 0) / 100  # Конвертируем из копеек
total = price * quantity

etree.SubElement(row_elem, "Сумма").text = str(int(total))  # ❌ Отбрасываем копейки
etree.SubElement(row_elem, "Цена").text = str(int(price))   # ❌ Отбрасываем копейки

Почему это критично:

  • Округление вниз приводит к расхождению сумм
  • НДС рассчитывается неверно
  • Контрольные суммы не сходятся
  • Невозможно сопоставить с УПД/счётом-фактурой

Пример:

Цена: 1234.56 руб → становится 1234 руб (потеря 56 коп)
Количество: 100 шт
Сумма должна быть: 123456.00 руб
Сумма получается: 123400 руб (потеря 56 руб!)

Рекомендация:

# ПРАВИЛЬНО: Сохранять копейки
price = pos.get("price", 0) / 100
total = price * quantity

etree.SubElement(row_elem, "Сумма").text = f"{total:.2f}"  # 2 знака после запятой
etree.SubElement(row_elem, "Цена").text = f"{price:.2f}"   # 2 знака после запятой

Решение:

  • Использовать Decimal для денежных расчётов
  • Форматировать с 2 знаками: f"{amount:.2f}"
  • НЕ использовать int() для денежных сумм

⚠️ СРЕДНЕ: 6. Лишние поля в строке товара

Местоположение: Строки 1036-1039

Проблема:

etree.SubElement(row_elem, "ТипЗапасов").text = "СобственныеТовары"
etree.SubElement(row_elem, "СчетУчета").text = "43"
etree.SubElement(row_elem, "СчетДоходов").text = "90.01.1"
etree.SubElement(row_elem, "СчетРасходов").text = "90.02.1"
etree.SubElement(row_elem, "СчетУчетаНДСПоРеализации").text = "90.03"

Почему это проблема:

  • Эти поля есть не во всех версиях ED
  • Не во всех прикладных решениях 1С используются эти поля
  • Если их нет в XSD - валидация провалится

Рекомендация:

  • Сделать эти поля опциональными (через настройку)
  • Или убрать совсем - 1С подставит по умолчанию
  • Или уточнить у клиента, нужны ли эти поля

TODO:

  • Проверить XSD целевой системы
  • Добавить флаг include_accounting_fields в конфиг
  • По умолчанию отключить

⚠️ СРЕДНЕ: 7. ВидЭД внутри ED-документа

Местоположение: Строки 967-968

Проблема:

# ВидЭД
etree.SubElement(doc, "ВидЭД").text = "ТОРГ12"

Почему это проблема:

  • Поле ВидЭД со значением "ТОРГ12" не входит в типовую схему ED 1.20
  • Это поле используется в УПД/ФормализованныхДокументах, но не в EnterpriseData
  • При валидации по XSD произойдёт ошибка

Рекомендация:

  • Убрать это поле из документа ED
  • Если нужно указать тип документа - использовать корректное поле из XSD
  • Возможно, это поле относится к другому формату (УПД)

Решение:

# УБРАТЬ эту строку
# etree.SubElement(doc, "ВидЭД").text = "ТОРГ12"

⚠️ НИЗКО: 8. Резервные заглушки дат

Местоположение: Строки 648-657

Проблема:

def _format_datetime(self, date_str: str) -> str:
    try:
        dt = datetime.fromisoformat(date_str.replace('Z', '+00:00'))
        return dt.strftime("%Y-%m-%dT%H:%M:%S")
    except Exception as e:
        logger.error(f"Ошибка форматирования даты: {e}")
        return datetime.now().strftime("%Y-%m-%dT%H:%M:%S")  # ❌ Подставляем текущую дату

Почему это проблема:

  • Если дата не парсится - подставляется текущая
  • Для первичных документов это искажение данных
  • Риск налоговых проблем (неверная дата = неверный период)

Рекомендация:

def _format_datetime(self, date_str: str) -> str:
    try:
        dt = datetime.fromisoformat(date_str.replace('Z', '+00:00'))
        return dt.strftime("%Y-%m-%dT%H:%M:%S")
    except Exception as e:
        logger.error(f"Критическая ошибка: некорректная дата '{date_str}': {e}")
        raise ValueError(f"Невозможно сформировать документ с некорректной датой: {date_str}")

Решение:

  • Поднимать исключение при ошибке парсинга даты
  • НЕ подставлять резервные значения для критичных полей
  • Блокировать генерацию XML с некорректными данными

Приоритеты исправления

🔴 СРОЧНО (блокируют импорт в 1С):

  1. ✅ Потеря копеек - исправить форматирование денежных сумм
  2. ⚠️ GUID справочников - убрать жёсткие UUID или получать из базы
  3. ⚠️ Банк/счёт - исправить структуру ссылок
  4. ✅ ВидЭД - убрать поле

🟡 ВАЖНО (могут вызвать проблемы):

  1. ⚠️ Структура валюты - сверить с XSD клиента
  2. ⚠️ Договор - упростить или вынести в отдельный блок
  3. ⚠️ Счета учёта - сделать опциональными

🟢 ЖЕЛАТЕЛЬНО (улучшение качества):

  1. ✅ Заглушки дат - поднимать исключения вместо fallback

Рекомендуемый план действий

Этап 1: Критичные исправления (сегодня)

  • Исправить форматирование денежных сумм (добавить .2f)
  • Убрать поле ВидЭД
  • Заменить fallback даты на исключение

Этап 2: Проверка со стороны клиента (завтра)

  • Получить актуальный XSD EnterpriseData 1.20 от клиента
  • Проверить структуру валюты/банков/договоров в реальной схеме
  • Уточнить: нужны ли счета учёта в строках?

Этап 3: Адаптация под реальную схему (2-3 дня)

  • Убрать жёсткие GUID, использовать только классификаторы
  • Исправить структуру банка (убрать Ссылку, оставить БИК)
  • Упростить договор или вынести в отдельный блок
  • Сделать счета учёта опциональными

Этап 4: Тестирование (1 день)

  • Тестовый импорт в реальную 1С клиента
  • Проверка контрольных сумм
  • Проверка НДС
  • Сопоставление с УПД/счетами-фактурами

Скептическая оценка анализа ИИ

✅ Что верно на 100%:

  • Потеря копеек - КРИТИЧНО, будет ошибка в суммах
  • GUID справочников - КРИТИЧНО, не будет работать в другой базе
  • ВидЭД - верно, этого поля нет в ED
  • Заглушки дат - верно, риск искажения данных

⚠️ Что нужно проверить:

  • Структура валюты - зависит от версии XSD (нужен реальный XSD)
  • Банк/счёт - вероятно верно, но нужна проверка по документации
  • Договор - может быть по-разному в разных версиях 1С
  • Счета учёта - зависит от конфигурации 1С клиента

📋 Что точно нужно сделать:

  1. Получить от клиента XSD EnterpriseData 1.20 их версии
  2. Протестировать импорт в реальной базе
  3. Исправить копейки и ВидЭД (это точно ошибки)
  4. Остальное - после проверки по реальной схеме

Вывод

Генератор работоспособен, но имеет критичные проблемы для продакшена:

  • ✅ Структура документа в целом корректна
  • ❌ Денежные суммы теряют копейки
  • ⚠️ Жёсткие GUID не будут работать в чужой базе
  • ⚠️ Структура может не совпадать с XSD клиента

Рекомендация:

  1. Срочно исправить копейки и убрать ВидЭД
  2. Получить XSD от клиента
  3. Протестировать в реальной базе
  4. Доработать под реальную схему

Риск: Без тестирования в реальной 1С клиента есть высокая вероятность ошибок импорта.