Dobavlen funkcional importa i eksporta klientov s validaciey i umnym sliyaniem kontaktov

This commit is contained in:
2026-01-03 13:33:34 +03:00
parent 208c6b55de
commit 3248fadffa
7 changed files with 695 additions and 19 deletions

View File

@@ -737,13 +737,76 @@ def customer_import(request):
"""
Импорт клиентов из CSV/Excel файла.
"""
import os
from pathlib import Path
from django.conf import settings
from .services.import_export import CustomerImporter
if request.method == 'POST':
file = request.FILES.get('file')
update_existing = request.POST.get('update_existing') == 'on'
if not file:
messages.error(request, 'Файл не был загружен.')
return redirect('customers:customer-import')
# Выполняем импорт
importer = CustomerImporter()
# TODO: Обработка загруженного файла
messages.info(request, 'Функция импорта в разработке')
return redirect('customers:customer-list')
result = importer.import_from_file(file, update_existing=update_existing)
# Формируем сообщения о результате
if result['success']:
success_parts = []
if result['created'] > 0:
success_parts.append(f"создано {result['created']}")
if result['enriched'] > 0:
success_parts.append(f"дополнено {result['enriched']}")
if result['updated'] > 0:
success_parts.append(f"обновлено {result['updated']}")
success_msg = f"Импорт завершён: {', '.join(success_parts) if success_parts else 'нет изменений'}"
if result.get('duplicate_count', 0) > 0:
success_msg += f", пропущено дубликатов: {result['duplicate_count']}"
if result.get('conflicts_resolved', 0) > 0:
success_msg += f", создано альтернативных контактов: {result['conflicts_resolved']}"
messages.success(request, success_msg)
else:
messages.error(request, result['message'])
# Если есть реальные ошибки валидации - генерируем файл
if result.get('real_error_count', 0) > 0:
error_file_data = importer.generate_error_file()
if error_file_data:
content, filename = error_file_data
# Сохраняем временный файл
temp_dir = Path(settings.MEDIA_ROOT) / 'temp_imports'
temp_dir.mkdir(parents=True, exist_ok=True)
temp_file_path = temp_dir / filename
with open(temp_file_path, 'wb') as f:
f.write(content)
# Сохраняем путь в сессии
request.session['import_error_file'] = str(temp_file_path)
request.session['import_error_filename'] = filename
messages.warning(
request,
f'Обнаружено {result["real_error_count"]} ошибок валидации. '
f'Скачайте файл с ошибками для исправления.'
)
# Передаём результаты в шаблон
context = {
'title': 'Импорт клиентов',
'import_result': result,
'has_error_file': 'import_error_file' in request.session,
}
return render(request, 'customers/customer_import.html', context)
context = {
'title': 'Импорт клиентов',
@@ -751,6 +814,71 @@ def customer_import(request):
return render(request, 'customers/customer_import.html', context)
@login_required
@manager_or_owner_required
def customer_import_download_errors(request):
"""
Скачивание файла с ошибками импорта и немедленное удаление.
"""
import os
from django.http import FileResponse, Http404
file_path = request.session.get('import_error_file')
filename = request.session.get('import_error_filename', 'errors.csv')
if not file_path or not os.path.exists(file_path):
messages.error(request, 'Файл с ошибками не найден или уже был удалён.')
return redirect('customers:customer-import')
try:
# Открываем файл для чтения
response = FileResponse(
open(file_path, 'rb'),
as_attachment=True,
filename=filename
)
# Удаляем из сессии
del request.session['import_error_file']
del request.session['import_error_filename']
# Планируем удаление файла после отправки
# (FileResponse закроет файл автоматически, затем удаляем)
def cleanup_file():
try:
if os.path.exists(file_path):
os.remove(file_path)
except Exception:
pass
# Django FileResponse автоматически закрывает файл после отправки
# Используем middleware или сигнал для очистки, но проще - удалим сразу после response
# Поскольку FileResponse читает файл в память при малом размере, удаляем сразу
import atexit
atexit.register(cleanup_file)
# Альтернатива: читаем файл в память и сразу удаляем
with open(file_path, 'rb') as f:
file_content = f.read()
# Удаляем файл немедленно
try:
os.remove(file_path)
except Exception:
pass
# Возвращаем содержимое из памяти
from django.http import HttpResponse
response = HttpResponse(file_content, content_type='application/octet-stream')
response['Content-Disposition'] = f'attachment; filename="{filename}"'
return response
except Exception as e:
messages.error(request, f'Ошибка при скачивании файла: {str(e)}')
return redirect('customers:customer-import')
@login_required
@manager_or_owner_required
def customer_export(request):