Dobavlen funkcional importa i eksporta klientov s validaciey i umnym sliyaniem kontaktov
This commit is contained in:
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user