Добавлен настраиваемый экспорт клиентов с выбором полей и форматов
Реализован полностью новый функционал экспорта клиентов с возможностью
выбора полей, формата файла (CSV/XLSX) и сохранением предпочтений.
Ключевые изменения:
1. CustomerExporter (import_export.py):
- Полностью переписан класс с поддержкой динамического выбора полей
- Добавлена конфигурация AVAILABLE_FIELDS с метаданными полей
- Реализован метод get_available_fields() для фильтрации по ролям
- Новый метод export_to_xlsx() с автоподстройкой ширины столбцов
- Форматирование ContactChannel с переводами строк
- Поддержка фильтрации queryset
2. CustomerExportForm (forms.py):
- Динамическое создание checkbox полей на основе роли пользователя
- Выбор формата файла (CSV/XLSX) через radio buttons
- Валидация выбора хотя бы одного поля
3. View customer_export (views.py):
- КРИТИЧНО: Изменён декоратор с @manager_or_owner_required на @owner_required
- Обработка GET (редирект) и POST запросов
- Применение фильтров CustomerFilter из списка клиентов
- Оптимизация с prefetch_related('contact_channels')
- Сохранение настроек экспорта в session
4. UI изменения:
- Создан шаблон customer_export_modal.html с модальным окном
- Обновлён customer_list.html: кнопка экспорта с проверкой роли
- JavaScript для восстановления сохранённых настроек из session
- Отображение количества экспортируемых клиентов
- Бейдж "Только для владельца" на поле баланса кошелька
Безопасность:
- Экспорт доступен ТОЛЬКО владельцу тенанта (OWNER) и superuser
- Поле "Баланс кошелька" скрыто от менеджеров на уровне формы
- Двойная проверка роли при экспорте баланса
- Кнопка экспорта скрыта в UI для всех кроме owner/superuser
Функциональность:
- Выбор полей: ID, имя, email, телефон, заметки, каналы связи, баланс, дата создания
- Форматы: CSV (с BOM для Excel) и XLSX
- Учёт текущих фильтров и поиска из списка клиентов
- Сохранение предпочтений между экспортами в session
- Исключение системного клиента из экспорта
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@ from django.db.models.functions import Greatest, Coalesce
|
||||
from django.http import JsonResponse
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from user_roles.decorators import manager_or_owner_required
|
||||
from user_roles.decorators import manager_or_owner_required, owner_required
|
||||
import phonenumbers
|
||||
import json
|
||||
from decimal import Decimal
|
||||
@@ -80,11 +80,18 @@ def customer_list(request):
|
||||
page_number = request.GET.get('page')
|
||||
page_obj = paginator.get_page(page_number)
|
||||
|
||||
# Подготовка формы экспорта и настроек из сессии
|
||||
from .forms import CustomerExportForm
|
||||
export_form = CustomerExportForm(user=request.user)
|
||||
export_preferences = request.session.get('customer_export_preferences', {})
|
||||
|
||||
context = {
|
||||
'page_obj': page_obj,
|
||||
'query': query,
|
||||
'total_customers': paginator.count, # Используем count из paginator, чтобы избежать дублирования SQL запроса
|
||||
'filter': customer_filter, # Добавляем фильтр в контекст
|
||||
'export_form': export_form, # Форма экспорта для модального окна
|
||||
'export_preferences': export_preferences, # Сохранённые настройки экспорта
|
||||
}
|
||||
return render(request, 'customers/customer_list.html', context)
|
||||
|
||||
@@ -886,12 +893,65 @@ def customer_import_download_errors(request):
|
||||
|
||||
|
||||
@login_required
|
||||
@manager_or_owner_required
|
||||
@owner_required
|
||||
def customer_export(request):
|
||||
"""
|
||||
Экспорт клиентов в CSV файл.
|
||||
Экспорт клиентов в CSV/XLSX файл.
|
||||
|
||||
GET: Перенаправление на список клиентов
|
||||
POST: Обработка экспорта с выбранными полями и форматом
|
||||
|
||||
Поддерживает фильтрацию - экспортирует только клиентов, соответствующих текущим фильтрам.
|
||||
Доступен только владельцу (OWNER) и superuser.
|
||||
"""
|
||||
from .services.import_export import CustomerExporter
|
||||
|
||||
exporter = CustomerExporter()
|
||||
return exporter.export_to_csv()
|
||||
from .forms import CustomerExportForm
|
||||
from .filters import CustomerFilter
|
||||
|
||||
# Базовый queryset (исключаем системного клиента)
|
||||
queryset = Customer.objects.filter(is_system_customer=False)
|
||||
|
||||
# Применяем фильтры (та же логика что в customer_list)
|
||||
customer_filter = CustomerFilter(request.GET, queryset=queryset)
|
||||
filtered_queryset = customer_filter.qs
|
||||
|
||||
# GET запрос: перенаправление на список клиентов
|
||||
if request.method != 'POST':
|
||||
messages.info(
|
||||
request,
|
||||
'Используйте кнопку "Экспорт" в списке клиентов для настройки экспорта.'
|
||||
)
|
||||
return redirect('customers:customer-list')
|
||||
|
||||
# POST запрос: обработка экспорта
|
||||
form = CustomerExportForm(request.POST, user=request.user)
|
||||
|
||||
if not form.is_valid():
|
||||
messages.error(request, 'Ошибка в настройках экспорта. Выберите хотя бы одно поле.')
|
||||
return redirect('customers:customer-list')
|
||||
|
||||
# Получение конфигурации экспорта
|
||||
selected_fields = form.cleaned_data['selected_fields']
|
||||
export_format = form.cleaned_data['export_format']
|
||||
|
||||
# Сохранение настроек в сессии
|
||||
request.session['customer_export_preferences'] = {
|
||||
'selected_fields': selected_fields,
|
||||
'format': export_format,
|
||||
}
|
||||
|
||||
# Оптимизация запроса (prefetch contact channels)
|
||||
filtered_queryset = filtered_queryset.prefetch_related('contact_channels').order_by('-created_at')
|
||||
|
||||
# Создание экспортера с отфильтрованным queryset
|
||||
exporter = CustomerExporter(
|
||||
queryset=filtered_queryset,
|
||||
selected_fields=selected_fields,
|
||||
user=request.user
|
||||
)
|
||||
|
||||
# Генерация и возврат файла экспорта
|
||||
if export_format == 'xlsx':
|
||||
return exporter.export_to_xlsx()
|
||||
else:
|
||||
return exporter.export_to_csv()
|
||||
|
||||
Reference in New Issue
Block a user