from django import forms from django.core.exceptions import ValidationError from phonenumber_field.formfields import PhoneNumberField from phonenumber_field.widgets import PhoneNumberPrefixWidget from .models import Customer, ContactChannel class CustomerForm(forms.ModelForm): phone = PhoneNumberField( region='BY', required=False, help_text='Формат: +375XXXXXXXXX или 80XXXXXXXXX', widget=forms.TextInput(attrs={'placeholder': '+375XXXXXXXXX'}) ) class Meta: model = Customer fields = ['name', 'email', 'phone', 'notes'] exclude = ['is_system_customer'] widgets = { 'notes': forms.Textarea(attrs={'rows': 3}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Ensure phone displays in E.164 format if self.instance and self.instance.phone: self.initial['phone'] = str(self.instance.phone) for field_name, field in self.fields.items(): if field_name == 'notes': # Textarea already has rows=3 from widget, just add class field.widget.attrs.update({'class': 'form-control'}) else: # Regular input fields get form-control class field.widget.attrs.update({'class': 'form-control'}) def clean_email(self): """Нормализует пустые значения email в None""" email = self.cleaned_data.get('email') if not email: return None return email def clean_phone(self): """Нормализует пустые значения телефона в None""" phone = self.cleaned_data.get('phone') if not phone: return None return phone def clean(self): """Дополнительная валидация формы""" cleaned_data = super().clean() # Защита от редактирования системного клиента if self.instance and self.instance.pk and self.instance.is_system_customer: raise ValidationError( 'Системный клиент не может быть изменен. ' 'Он необходим для корректной работы системы и создается автоматически.' ) return cleaned_data class ContactChannelForm(forms.ModelForm): """Форма для добавления/редактирования канала связи""" class Meta: model = ContactChannel fields = ['channel_type', 'value', 'is_primary', 'notes'] widgets = { 'channel_type': forms.Select(attrs={'class': 'form-select'}), 'value': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '@username, номер и т.д.'}), 'notes': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Личный аккаунт, рабочий...'}), 'is_primary': forms.CheckboxInput(attrs={'class': 'form-check-input'}), } def clean_value(self): value = self.cleaned_data.get('value', '').strip() channel_type = self.cleaned_data.get('channel_type') if not value: raise ValidationError('Значение не может быть пустым') # Проверка уникальности комбинации channel_type + value qs = ContactChannel.objects.filter(channel_type=channel_type, value=value) if self.instance.pk: qs = qs.exclude(pk=self.instance.pk) if qs.exists(): type_display = dict(ContactChannel.CHANNEL_TYPES).get(channel_type, channel_type) raise ValidationError(f'Такой {type_display} уже существует у другого клиента') return value class CustomerExportForm(forms.Form): """Форма настройки экспорта клиентов""" FORMAT_CHOICES = [ ('csv', 'CSV'), ('xlsx', 'Excel (XLSX)'), ] export_format = forms.ChoiceField( choices=FORMAT_CHOICES, widget=forms.RadioSelect(attrs={'class': 'form-check-input'}), initial='csv', label='Формат файла' ) def __init__(self, *args, user=None, **kwargs): super().__init__(*args, **kwargs) # Получаем доступные поля на основе роли пользователя from .services.import_export import CustomerExporter available_fields = CustomerExporter.get_available_fields(user) # Динамически создаём checkbox поля for field_key, field_info in available_fields.items(): self.fields[f'field_{field_key}'] = forms.BooleanField( required=False, label=field_info['label'], initial=field_key in CustomerExporter.DEFAULT_FIELDS, widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}) ) def clean(self): cleaned_data = super().clean() # Собираем выбранные поля selected_fields = [ key.replace('field_', '') for key, value in cleaned_data.items() if key.startswith('field_') and value ] if not selected_fields: raise ValidationError('Выберите хотя бы одно поле для экспорта') cleaned_data['selected_fields'] = selected_fields return cleaned_data