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

396 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Анализ генератора XML EnterpriseData 1.20
## Дата анализа: 6 ноября 2025 г.
## Проверенные проблемы и их статус
### ❌ КРИТИЧНО: 1. Жёстко прошитые GUID справочников
**Местоположение:**
- Строки 773-776 (валюта RUB)
- Строки 878, 1027 (страна Россия)
- Строки 990-994 (единица измерения "шт")
**Проблема:**
```python
# Валюта - жёстко прошитый 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 не существуют
- Импорт завершится ошибкой "Объект не найден"
**Рекомендация:**
```python
# ПРАВИЛЬНО: Передавать только код и наименование, без Ссылки
# Либо позволить 1С создать объект автоматически
<Валюта>
<!-- Убрать <Ссылка> если объект не существует -->
<ДанныеКлассификатора>
<Код>643</Код>
<НаименованиеПолное>Российский рубль</НаименованиеПолное>
<КодЦифровой>643</КодЦифровой>
<КодСимвольный>RUB</КодСимвольный>
</ДанныеКлассификатора>
</Валюта>
```
**Решение:**
1. Либо получать GUID из конфигурации целевой базы
2. Либо использовать флаг "создавать отсутствующие объекты"
3. Либо убрать `<Ссылка>` и передавать только классификаторы
---
### ⚠️ КРИТИЧНО: 2. Структура валюты/банков не соответствует XSD
**Местоположение:** Строки 767-780
**Проблема:**
```python
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
**Проблема:**
```python
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 для обоих объектов неверно
**Рекомендация:**
```python
# ПРАВИЛЬНО: Разные 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
**Проблема:**
```python
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
**Рекомендация:**
```xml
<!-- Вариант 1: Только ссылка (если договор уже есть) -->
<Договор>
<Ссылка>uuid-договора</Ссылка>
</Договор>
<!-- Вариант 2: Полный объект в отдельном блоке Message -->
<Message>
<Body>
<Справочник.ДоговорыКонтрагентов>
<Ссылка>uuid</Ссылка>
<ВидДоговора>СПокупателем</ВидДоговора>
<!-- ... -->
</Справочник.ДоговорыКонтрагентов>
<Документ.РеализацияТоваровУслуг>
<Договор>
<Ссылка>uuid</Ссылка> <!-- Ссылка на объект выше -->
</Договор>
</Документ.РеализацияТоваровУслуг>
</Body>
</Message>
```
**TODO:**
- [ ] Уточнить у клиента: договоры уже есть в базе или нужно создавать?
- [ ] Если создавать - вынести договор в отдельный блок Message
- [ ] Если использовать существующие - передавать только Ссылка
---
### ❌ КРИТИЧНО: 5. Потеря копеек
**Местоположение:** Строки 1012-1014
**Проблема:**
```python
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 руб!)
```
**Рекомендация:**
```python
# ПРАВИЛЬНО: Сохранять копейки
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
**Проблема:**
```python
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
**Проблема:**
```python
# ВидЭД
etree.SubElement(doc, "ВидЭД").text = "ТОРГ12"
```
**Почему это проблема:**
- Поле `ВидЭД` со значением "ТОРГ12" не входит в типовую схему ED 1.20
- Это поле используется в УПД/ФормализованныхДокументах, но не в EnterpriseData
- При валидации по XSD произойдёт ошибка
**Рекомендация:**
- Убрать это поле из документа ED
- Если нужно указать тип документа - использовать корректное поле из XSD
- Возможно, это поле относится к другому формату (УПД)
**Решение:**
```python
# УБРАТЬ эту строку
# etree.SubElement(doc, "ВидЭД").text = "ТОРГ12"
```
---
### ⚠️ НИЗКО: 8. Резервные заглушки дат
**Местоположение:** Строки 648-657
**Проблема:**
```python
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") # ❌ Подставляем текущую дату
```
**Почему это проблема:**
- Если дата не парсится - подставляется текущая
- Для первичных документов это искажение данных
- Риск налоговых проблем (неверная дата = неверный период)
**Рекомендация:**
```python
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. ✅ **ВидЭД** - убрать поле
### 🟡 ВАЖНО (могут вызвать проблемы):
5. ⚠️ **Структура валюты** - сверить с XSD клиента
6. ⚠️ **Договор** - упростить или вынести в отдельный блок
7. ⚠️ **Счета учёта** - сделать опциональными
### 🟢 ЖЕЛАТЕЛЬНО (улучшение качества):
8. ✅ **Заглушки дат** - поднимать исключения вместо 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С клиента есть **высокая вероятность** ошибок импорта.