Улучшение модели 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 -*-
from django import forms
from django.forms import inlineformset_factory
from phonenumber_field.formfields import PhoneNumberField
from .models import Order, OrderItem, Transaction, Address, OrderStatus, Recipient, Delivery
from customers.models import Customer
from products.models import Product, ProductKit
@@ -47,11 +48,20 @@ class OrderForm(forms.ModelForm):
label='Имя получателя'
)
recipient_phone = forms.CharField(
max_length=20,
recipient_phone = PhoneNumberField(
region=None,
required=False,
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:
self.fields['recipient_source'].initial = 'new'
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:
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 phonenumber_field.modelfields import PhoneNumberField
class Recipient(models.Model):
@@ -12,10 +13,18 @@ class Recipient(models.Model):
help_text="ФИО или название организации получателя"
)
phone = models.CharField(
max_length=20,
phone = PhoneNumberField(
region=None,
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): Словарь с данными из формы
- recipient_name
- recipient_phone
- recipient_notes (опционально)
Returns:
Recipient: Новый объект получателя (не сохраненный в БД)
@@ -54,6 +55,7 @@ class AddressService:
recipient = Recipient(
name=form_data.get('recipient_name', ''),
phone=form_data.get('recipient_phone', ''),
notes=form_data.get('recipient_notes', '') or None,
)
return recipient
@@ -90,7 +92,8 @@ class AddressService:
# Если режим "новый получатель"
if recipient_source == 'new':
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:
return None
@@ -105,18 +108,23 @@ class AddressService:
# Обновляем существующего получателя
db_order.recipient.name = name
db_order.recipient.phone = phone
db_order.recipient.notes = notes
# Сохранять будем в views.py, здесь просто возвращаем объект
return db_order.recipient
except Order.DoesNotExist:
pass
# Проверяем, есть ли уже такой получатель в БД (по имени и телефону)
# Проверяем, есть ли уже такой получатель в БД (по имени и нормализованному телефону)
# PhoneNumberField автоматически нормализует телефон, поэтому можно искать напрямую
existing_recipient = Recipient.objects.filter(
name=name,
name__iexact=name.strip(),
phone=phone
).first()
if existing_recipient:
# Обновляем заметки, если они изменились
if existing_recipient.notes != notes:
existing_recipient.notes = notes
return existing_recipient
# Создаем нового получателя

View File

@@ -658,11 +658,28 @@
Телефон получателя
</label>
{{ 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 %}
<div class="text-danger">{{ form.recipient_phone.errors }}</div>
{% endif %}
</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>