feat: добавлена валидация при массовой генерации XML

- Каждая отгрузка теперь валидируется перед генерацией
- При наличии критических ошибок валидации создается текстовый отчет
- Отчет содержит:
  * Список отгрузок с критическими ошибками
  * Детальное описание каждой ошибки
  * Отгрузки не найденные в системе
  * Ошибки обработки
  * Список успешно прошедших валидацию
- Отгрузки с ошибками исключаются из генерации XML
- Успешно прошедшие валидацию отгрузки обрабатываются как обычно
- Улучшена обработка ошибок и информативность сообщений
This commit is contained in:
admin i 2025-11-06 14:11:59 +03:00
parent b29bf9a354
commit 7866009e24

141
bot.py
View File

@ -11,6 +11,7 @@ import os
import sys
import logging
from pathlib import Path
from datetime import datetime
from telegram import (
Update,
@ -1426,10 +1427,14 @@ async def batch_generation_process(
try:
moysklad_client = get_moysklad_client(user_id)
# Сбор данных по всем отгрузкам
# Сбор данных по всем отгрузкам с валидацией
from modules.validator import OrderValidator, ValidationLevel
validator = OrderValidator()
orders_data = []
not_found = []
errors = []
validation_errors = {} # Словарь {order_num: [список ошибок]}
for idx, order_num in enumerate(order_numbers, 1):
try:
@ -1455,13 +1460,141 @@ async def batch_generation_process(
# Получаем полные данные
order_data = moysklad_client.get_full_order_data(order_id)
orders_data.append(order_data)
# ВАЛИДАЦИЯ заказа
validation_results = validator.validate_order(order_data)
# Проверяем наличие критических ошибок
critical_errors = [
r for r in validation_results
if r.level == ValidationLevel.CRITICAL
]
if critical_errors:
# Сохраняем ошибки для отчета
validation_errors[order_num] = [
f"{r.field}: {r.message}"
for r in critical_errors
]
logger.warning(
f"Отгрузка {order_num} не прошла валидацию: "
f"{len(critical_errors)} критических ошибок"
)
else:
# Валидация пройдена - добавляем к обработке
orders_data.append(order_data)
except Exception as e:
logger.error(f"Ошибка обработки {order_num}: {e}")
errors.append(f"{order_num}: {str(e)[:50]}")
# Проверка результатов
# Если есть ошибки валидации, отправляем текстовый отчет
if validation_errors:
# Создаём текстовый файл с ошибками
report_lines = [
"ОТЧЕТ О ВАЛИДАЦИИ ОТГРУЗОК",
"=" * 50,
f"Дата: {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}",
f"Всего обработано: {len(order_numbers)}",
f"С ошибками валидации: {len(validation_errors)}",
"",
"КРИТИЧЕСКИЕ ОШИБКИ ВАЛИДАЦИИ:",
""
]
for order_num, err_list in validation_errors.items():
report_lines.append(f"Отгрузка: {order_num}")
report_lines.append("-" * 40)
for err in err_list:
report_lines.append(f" • {err}")
report_lines.append("")
# Добавляем информацию о не найденных
if not_found:
report_lines.append("")
report_lines.append("НЕ НАЙДЕНЫ:")
report_lines.append("-" * 40)
for order_num in not_found:
report_lines.append(f" • {order_num}")
report_lines.append("")
# Добавляем прочие ошибки
if errors:
report_lines.append("")
report_lines.append("ОШИБКИ ОБРАБОТКИ:")
report_lines.append("-" * 40)
for err in errors:
report_lines.append(f" • {err}")
report_lines.append("")
# Статистика успешных
if orders_data:
report_lines.append("")
report_lines.append(
f"Успешно прошли валидацию: {len(orders_data)}"
)
report_lines.append("-" * 40)
for order_data in orders_data:
order_name = order_data['order'].get('name', 'N/A')
report_lines.append(f" ✓ {order_name}")
# Сохраняем в файл
output_dir = Path("temp_documents")
output_dir.mkdir(exist_ok=True)
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
report_filename = f"validation_errors_{timestamp}.txt"
report_path = output_dir / report_filename
with open(report_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(report_lines))
# Формируем сообщение
error_msg = (
f"❌ <b>Обнаружены ошибки валидации</b>\n\n"
f"📊 Статистика:\n"
f" • Обработано: {len(order_numbers)}\n"
f" • С ошибками: {len(validation_errors)}\n"
)
if orders_data:
error_msg += f" • Прошли валидацию: {len(orders_data)}\n"
if not_found:
error_msg += f" • Не найдено: {len(not_found)}\n"
error_msg += (
"\n⚠️ Отгрузки с ошибками валидации не будут обработаны.\n"
"Подробный отчет во вложенном файле."
)
await progress_msg.edit_text(
error_msg,
parse_mode='HTML'
)
# Отправляем файл с ошибками
with open(report_path, 'rb') as f:
await update.message.reply_document(
document=f,
filename=report_filename,
caption=(
"📄 Отчет об ошибках валидации\n\n"
"Исправьте указанные ошибки в МойСклад "
"и повторите генерацию."
),
parse_mode='HTML'
)
# Удаляем временный файл
os.remove(report_path)
# Если нет ни одной валидной отгрузки - завершаем
if not orders_data:
await update.message.reply_text(
"Попробуйте ещё раз или вернитесь в главное меню:",
reply_markup=get_main_keyboard()
)
return MAIN_MENU
# Проверка результатов (если совсем ничего не найдено)
if not orders_data:
error_text = (
"❌ <b>Не удалось обработать ни одной отгрузки</b>\n\n"
@ -1469,7 +1602,7 @@ async def batch_generation_process(
if not_found:
error_text += f"Не найдено: {', '.join(not_found)}\n"
if errors:
error_text += f"\nОшибки:\n" + "\n".join(errors[:5])
error_text += "\nОшибки:\n" + "\n".join(errors[:5])
await progress_msg.edit_text(
error_text,