Улучшение модели Recipient: PhoneNumberField и поле notes

- Заменено поле phone с CharField на PhoneNumberField для автоматической нормализации телефонов
- Убран регион BY, установлен region=None для универсальности (поддержка номеров разных стран)
- Добавлено поле notes для дополнительной информации о получателе (мессенджеры, соцсети и т.д.)
- Улучшена логика поиска существующих получателей:
  * Использование нормализованного телефона из PhoneNumberField
  * Регистронезависимый поиск по имени (name__iexact)
  * Обновление notes при нахождении существующего получателя
- Обновлена форма OrderForm для работы с PhoneNumberField и новым полем notes
- Обновлен шаблон order_form.html для отображения нового поля
- Созданы миграции для изменений модели
This commit is contained in:
2025-12-25 11:44:18 +03:00
parent 298d797286
commit 2f8a421e64
7 changed files with 121 additions and 10 deletions

View File

@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django import forms from django import forms
from django.forms import inlineformset_factory from django.forms import inlineformset_factory
from phonenumber_field.formfields import PhoneNumberField
from .models import Order, OrderItem, Transaction, Address, OrderStatus, Recipient, Delivery from .models import Order, OrderItem, Transaction, Address, OrderStatus, Recipient, Delivery
from customers.models import Customer from customers.models import Customer
from products.models import Product, ProductKit from products.models import Product, ProductKit
@@ -47,11 +48,20 @@ class OrderForm(forms.ModelForm):
label='Имя получателя' label='Имя получателя'
) )
recipient_phone = forms.CharField( recipient_phone = PhoneNumberField(
max_length=20, region=None,
required=False, required=False,
widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Телефон получателя'}), widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Телефон получателя'}),
label='Телефон получателя' label='Телефон получателя',
help_text='Введите телефон в любом формате, будет автоматически преобразован'
)
recipient_notes = forms.CharField(
max_length=200,
required=False,
widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Мессенджер, соцсеть и т.д.'}),
label='Дополнительная информация',
help_text='Мессенджер, соцсеть или другая информация о получателе (необязательно)'
) )
# Поля для работы с адресом # Поля для работы с адресом
@@ -263,7 +273,8 @@ class OrderForm(forms.ModelForm):
else: else:
self.fields['recipient_source'].initial = 'new' self.fields['recipient_source'].initial = 'new'
self.fields['recipient_name'].initial = self.instance.recipient.name or '' self.fields['recipient_name'].initial = self.instance.recipient.name or ''
self.fields['recipient_phone'].initial = self.instance.recipient.phone or '' self.fields['recipient_phone'].initial = str(self.instance.recipient.phone) if self.instance.recipient.phone else ''
self.fields['recipient_notes'].initial = self.instance.recipient.notes or ''
else: else:
self.fields['other_recipient'].initial = False self.fields['other_recipient'].initial = False

View File

@@ -0,0 +1,24 @@
# Generated by Django 5.0.10 on 2025-12-24 21:48
import phonenumber_field.modelfields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('orders', '0004_make_delivery_time_optional'),
]
operations = [
migrations.AddField(
model_name='recipient',
name='additional_contact',
field=models.CharField(blank=True, help_text='Мессенджер, соцсеть или другая контактная информация (необязательно)', max_length=200, null=True, verbose_name='Дополнительный контакт'),
),
migrations.AlterField(
model_name='recipient',
name='phone',
field=phonenumber_field.modelfields.PhoneNumberField(help_text='Контактный телефон для связи с получателем. Введите в любом формате, будет автоматически преобразован', max_length=128, region='BY', verbose_name='Телефон получателя'),
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 5.0.10 on 2025-12-24 21:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('orders', '0005_recipient_additional_contact_alter_recipient_phone'),
]
operations = [
migrations.RenameField(
model_name='recipient',
old_name='additional_contact',
new_name='notes',
),
migrations.AlterField(
model_name='recipient',
name='notes',
field=models.CharField(blank=True, help_text='Мессенджер, соцсеть или другая информация о получателе (необязательно)', max_length=200, null=True, verbose_name='Дополнительная информация'),
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.0.10 on 2025-12-24 21:56
import phonenumber_field.modelfields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('orders', '0006_rename_additional_contact_to_notes'),
]
operations = [
migrations.AlterField(
model_name='recipient',
name='phone',
field=phonenumber_field.modelfields.PhoneNumberField(help_text='Контактный телефон для связи с получателем. Введите в любом формате, будет автоматически преобразован', max_length=128, region=None, verbose_name='Телефон получателя'),
),
]

View File

@@ -1,4 +1,5 @@
from django.db import models from django.db import models
from phonenumber_field.modelfields import PhoneNumberField
class Recipient(models.Model): class Recipient(models.Model):
@@ -12,10 +13,18 @@ class Recipient(models.Model):
help_text="ФИО или название организации получателя" help_text="ФИО или название организации получателя"
) )
phone = models.CharField( phone = PhoneNumberField(
max_length=20, region=None,
verbose_name="Телефон получателя", verbose_name="Телефон получателя",
help_text="Контактный телефон для связи с получателем" help_text="Контактный телефон для связи с получателем. Введите в любом формате, будет автоматически преобразован"
)
notes = models.CharField(
max_length=200,
blank=True,
null=True,
verbose_name="Дополнительная информация",
help_text="Мессенджер, соцсеть или другая информация о получателе (необязательно)"
) )
# Временные метки # Временные метки

View File

@@ -47,6 +47,7 @@ class AddressService:
form_data (dict): Словарь с данными из формы form_data (dict): Словарь с данными из формы
- recipient_name - recipient_name
- recipient_phone - recipient_phone
- recipient_notes (опционально)
Returns: Returns:
Recipient: Новый объект получателя (не сохраненный в БД) Recipient: Новый объект получателя (не сохраненный в БД)
@@ -54,6 +55,7 @@ class AddressService:
recipient = Recipient( recipient = Recipient(
name=form_data.get('recipient_name', ''), name=form_data.get('recipient_name', ''),
phone=form_data.get('recipient_phone', ''), phone=form_data.get('recipient_phone', ''),
notes=form_data.get('recipient_notes', '') or None,
) )
return recipient return recipient
@@ -90,7 +92,8 @@ class AddressService:
# Если режим "новый получатель" # Если режим "новый получатель"
if recipient_source == 'new': if recipient_source == 'new':
name = form_data.get('recipient_name', '').strip() name = form_data.get('recipient_name', '').strip()
phone = form_data.get('recipient_phone', '').strip() phone = form_data.get('recipient_phone', '')
notes = form_data.get('recipient_notes', '').strip() or None
if not name or not phone: if not name or not phone:
return None return None
@@ -105,18 +108,23 @@ class AddressService:
# Обновляем существующего получателя # Обновляем существующего получателя
db_order.recipient.name = name db_order.recipient.name = name
db_order.recipient.phone = phone db_order.recipient.phone = phone
db_order.recipient.notes = notes
# Сохранять будем в views.py, здесь просто возвращаем объект # Сохранять будем в views.py, здесь просто возвращаем объект
return db_order.recipient return db_order.recipient
except Order.DoesNotExist: except Order.DoesNotExist:
pass pass
# Проверяем, есть ли уже такой получатель в БД (по имени и телефону) # Проверяем, есть ли уже такой получатель в БД (по имени и нормализованному телефону)
# PhoneNumberField автоматически нормализует телефон, поэтому можно искать напрямую
existing_recipient = Recipient.objects.filter( existing_recipient = Recipient.objects.filter(
name=name, name__iexact=name.strip(),
phone=phone phone=phone
).first() ).first()
if existing_recipient: if existing_recipient:
# Обновляем заметки, если они изменились
if existing_recipient.notes != notes:
existing_recipient.notes = notes
return existing_recipient return existing_recipient
# Создаем нового получателя # Создаем нового получателя

View File

@@ -658,11 +658,28 @@
Телефон получателя Телефон получателя
</label> </label>
{{ form.recipient_phone }} {{ form.recipient_phone }}
{% if form.recipient_phone.help_text %}
<small class="form-text text-muted d-block">{{ form.recipient_phone.help_text }}</small>
{% endif %}
{% if form.recipient_phone.errors %} {% if form.recipient_phone.errors %}
<div class="text-danger">{{ form.recipient_phone.errors }}</div> <div class="text-danger">{{ form.recipient_phone.errors }}</div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div class="col-md-12">
<div class="mb-3">
<label for="{{ form.recipient_notes.id_for_label }}" class="form-label">
{{ form.recipient_notes.label }}
</label>
{{ form.recipient_notes }}
{% if form.recipient_notes.help_text %}
<small class="form-text text-muted d-block">{{ form.recipient_notes.help_text }}</small>
{% endif %}
{% if form.recipient_notes.errors %}
<div class="text-danger">{{ form.recipient_notes.errors }}</div>
{% endif %}
</div>
</div>
</div> </div>
</div> </div>
</div> </div>