diff --git a/myproject/customers/admin.py b/myproject/customers/admin.py index 5a2c56c..ec37b18 100644 --- a/myproject/customers/admin.py +++ b/myproject/customers/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin from django.db import models -from .models import Customer, Address +from .models import Customer class IsVipFilter(admin.SimpleListFilter): @@ -21,14 +21,6 @@ class IsVipFilter(admin.SimpleListFilter): return queryset -class AddressInline(admin.TabularInline): - """Inline для управления адресами клиента в интерфейсе администратора""" - model = Address - extra = 1 - verbose_name = "Адрес доставки" - verbose_name_plural = "Адреса доставки" - - @admin.register(Customer) class CustomerAdmin(admin.ModelAdmin): """Административный интерфейс для управления клиентами цветочного магазина""" @@ -69,55 +61,3 @@ class CustomerAdmin(admin.ModelAdmin): 'classes': ('collapse',) }), ) - - inlines = [AddressInline] - - -@admin.register(Address) -class AddressAdmin(admin.ModelAdmin): - """Административный интерфейс для управления адресами доставки""" - list_display = ( - 'recipient_name', - 'recipient_phone', - 'full_address', - 'customer', - 'district', - 'confirm_address_with_recipient', - 'is_default' - ) - list_filter = ( - 'is_default', - 'confirm_address_with_recipient', - 'district', - 'created_at' - ) - search_fields = ( - 'recipient_name', - 'street', - 'building_number', - 'customer__name', - 'customer__email' - ) - ordering = ('-is_default', '-created_at') - readonly_fields = ('created_at', 'updated_at') - - fieldsets = ( - ('Информация о получателе', { - 'fields': ('customer', 'recipient_name', 'recipient_phone') - }), - ('Адрес доставки', { - 'fields': ('street', 'building_number', 'apartment_number', 'district') - }), - ('Дополнительная информация', { - 'fields': ('delivery_instructions', 'confirm_address_with_recipient'), - 'classes': ('collapse',) - }), - ('Статус', { - 'fields': ('is_default',), - 'classes': ('collapse',) - }), - ('Даты', { - 'fields': ('created_at', 'updated_at'), - 'classes': ('collapse',) - }), - ) diff --git a/myproject/customers/migrations/0002_remove_address_model.py b/myproject/customers/migrations/0002_remove_address_model.py new file mode 100644 index 0000000..0c33e49 --- /dev/null +++ b/myproject/customers/migrations/0002_remove_address_model.py @@ -0,0 +1,17 @@ +# Generated by Django 5.0.10 on 2025-11-10 23:09 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('customers', '0001_initial'), + ('orders', '0003_remove_address_model'), + ] + + operations = [ + migrations.DeleteModel( + name='Address', + ), + ] diff --git a/myproject/customers/models.py b/myproject/customers/models.py index 04d77a6..ce8487a 100644 --- a/myproject/customers/models.py +++ b/myproject/customers/models.py @@ -193,109 +193,3 @@ class Customer(models.Model): """Увеличивает общую сумму покупок""" self.total_spent = self.total_spent + amount self.save(update_fields=['total_spent']) - - -class Address(models.Model): - """ - Модель адреса доставки для клиентов цветочного магазина в Минске. - Клиент может иметь несколько адресов для разных получателей. - """ - customer = models.ForeignKey( - Customer, - on_delete=models.CASCADE, - related_name='addresses', - verbose_name="Клиент" - ) - - # Address information for delivery in Minsk - recipient_name = models.CharField( - max_length=200, - verbose_name="Имя получателя", - help_text="Имя человека, которому будет доставлен заказ" - ) - - recipient_phone = PhoneNumberField( - blank=True, - null=True, - verbose_name="Телефон получателя", - help_text="Контактный телефон получателя для уточнения адреса" - ) - - street = models.CharField( - max_length=255, - verbose_name="Улица" - ) - - building_number = models.CharField( - max_length=20, - verbose_name="Номер здания" - ) - - apartment_number = models.CharField( - max_length=20, - blank=True, - null=True, - verbose_name="Номер квартиры/офиса" - ) - - district = models.CharField( - max_length=100, - blank=True, - null=True, - verbose_name="Район", - help_text="Район в Минске для удобства доставки" - ) - - # Additional information for delivery - delivery_instructions = models.TextField( - blank=True, - null=True, - verbose_name="Инструкции для доставки", - help_text="Дополнительные инструкции для курьера (домофон, подъезд и т.д.)" - ) - - confirm_address_with_recipient = models.BooleanField( - default=False, - verbose_name="Уточнить адрес у получателя", - help_text="Курьер должен уточнить адрес у получателя перед доставкой" - ) - - is_default = models.BooleanField( - default=False, - verbose_name="Адрес по умолчанию", - help_text="Использовать этот адрес для доставки по умолчанию" - ) - - # Timestamps - created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания") - updated_at = models.DateTimeField(auto_now=True, verbose_name="Дата обновления") - - class Meta: - verbose_name = "Адрес доставки" - verbose_name_plural = "Адреса доставки" - indexes = [ - models.Index(fields=['customer']), - models.Index(fields=['is_default']), - models.Index(fields=['district']), - ] - ordering = ['-is_default', '-created_at'] - - def save(self, *args, **kwargs): - if self.is_default: - # Если этот адрес устанавливается как адрес по умолчанию, снимаем флаг по умолчанию с других адресов этого клиента - Address.objects.filter(customer=self.customer, is_default=True).update(is_default=False) - super().save(*args, **kwargs) - - def __str__(self): - address_line = f"{self.street}, {self.building_number}" - if self.apartment_number: - address_line += f", кв/офис {self.apartment_number}" - return f"{self.recipient_name} - {address_line}, {self.customer.full_name}" - - @property - def full_address(self): - """Полный адрес для доставки""" - address = f"{self.street}, {self.building_number}" - if self.apartment_number: - address += f", кв/офис {self.apartment_number}" - return address diff --git a/myproject/customers/views.py b/myproject/customers/views.py index 91bdf3f..a7882ea 100644 --- a/myproject/customers/views.py +++ b/myproject/customers/views.py @@ -8,7 +8,7 @@ from django.views.decorators.http import require_http_methods from django.contrib.auth.decorators import login_required import phonenumbers import json -from .models import Customer, Address +from .models import Customer from .forms import CustomerForm diff --git a/myproject/orders/admin.py b/myproject/orders/admin.py index fa1eca1..ee0d85e 100644 --- a/myproject/orders/admin.py +++ b/myproject/orders/admin.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from django.contrib import admin -from .models import Order, OrderItem, Payment +from .models import Order, OrderItem, Payment, Address class PaymentInline(admin.TabularInline): @@ -228,3 +228,53 @@ class OrderItemAdmin(admin.ModelAdmin): 'classes': ('collapse',) }), ) + + +@admin.register(Address) +class AddressAdmin(admin.ModelAdmin): + """ + Админ-панель для управления адресами доставки заказов. + """ + list_display = [ + 'recipient_name', + 'recipient_phone', + 'full_address', + 'entrance', + 'floor', + 'confirm_address_with_recipient', + 'created_at', + ] + + list_filter = [ + 'confirm_address_with_recipient', + 'created_at', + ] + + search_fields = [ + 'recipient_name', + 'street', + 'building_number', + ] + + readonly_fields = ['created_at', 'updated_at'] + + fieldsets = ( + ('Информация о получателе', { + 'fields': ('recipient_name', 'recipient_phone') + }), + ('Адрес доставки', { + 'fields': ('street', 'building_number', 'apartment_number', 'entrance', 'floor') + }), + ('Доступ в здание', { + 'fields': ('intercom_code',), + 'classes': ('collapse',) + }), + ('Дополнительная информация', { + 'fields': ('delivery_instructions', 'confirm_address_with_recipient'), + 'classes': ('collapse',) + }), + ('Даты', { + 'fields': ('created_at', 'updated_at'), + 'classes': ('collapse',) + }), + ) diff --git a/myproject/orders/forms.py b/myproject/orders/forms.py index 7985c1a..d90ed55 100644 --- a/myproject/orders/forms.py +++ b/myproject/orders/forms.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- from django import forms from django.forms import inlineformset_factory -from .models import Order, OrderItem -from customers.models import Customer, Address +from .models import Order, OrderItem, Address +from customers.models import Customer from shops.models import Shop from products.models import Product, ProductKit @@ -10,6 +10,82 @@ from products.models import Product, ProductKit class OrderForm(forms.ModelForm): """Форма для создания и редактирования заказа""" + # Поля для ввода адреса + address_mode = forms.ChoiceField( + choices=[ + ('history', 'Выбрать из истории'), + ('new', 'Ввести новый адрес'), + ('empty', 'Без адреса (заполнить позже)'), + ], + initial='empty', + widget=forms.RadioSelect(attrs={'class': 'form-check-input'}), + required=False, + label='Способ указания адреса' + ) + + # Выбор адреса из истории + address_from_history = forms.ModelChoiceField( + queryset=Address.objects.none(), + required=False, + widget=forms.Select(attrs={'class': 'form-select'}), + label='Адрес из истории' + ) + + # Поля для ввода нового адреса + address_street = forms.CharField( + max_length=255, + required=False, + widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Улица'}), + label='Улица' + ) + + address_building_number = forms.CharField( + max_length=20, + required=False, + widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Номер дома'}), + label='Номер дома' + ) + + address_apartment_number = forms.CharField( + max_length=20, + required=False, + widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Квартира/офис'}), + label='Квартира/офис' + ) + + address_entrance = forms.CharField( + max_length=20, + required=False, + widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Подъезд'}), + label='Подъезд' + ) + + address_floor = forms.CharField( + max_length=20, + required=False, + widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Этаж'}), + label='Этаж' + ) + + address_intercom_code = forms.CharField( + max_length=100, + required=False, + widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Код домофона'}), + label='Код домофона' + ) + + address_delivery_instructions = forms.CharField( + required=False, + widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 3, 'placeholder': 'Инструкции для курьера'}), + label='Инструкции для доставки' + ) + + address_confirm_with_recipient = forms.BooleanField( + required=False, + widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}), + label='Уточнить адрес у получателя' + ) + class Meta: model = Order fields = [ @@ -46,7 +122,14 @@ class OrderForm(forms.ModelForm): field.widget.attrs.update({'class': 'form-check-input'}) elif isinstance(field.widget, forms.Textarea): field.widget.attrs.update({'class': 'form-control', 'rows': 3}) + elif isinstance(field.widget, forms.RadioSelect): + # RadioSelect не нуждается в доп классах (уже есть form-check-input) + pass + elif isinstance(field.widget, forms.Select): + # Select поля получают form-select + field.widget.attrs.update({'class': 'form-select'}) else: + # Остальные поля (TextInput, NumberInput, etc) field.widget.attrs.update({'class': 'form-control'}) # Select2 для поля customer с AJAX поиском (инициализируется отдельно в JS) @@ -81,6 +164,18 @@ class OrderForm(forms.ModelForm): self.fields['recipient_name'].required = False self.fields['recipient_phone'].required = False + # Инициализируем queryset для address_from_history + # Это будет переопределено в представлении после выбора клиента + if self.instance.pk and self.instance.customer: + # При редактировании заказа загружаем историю адресов этого клиента + customer_orders = Order.objects.filter( + customer=self.instance.customer, + delivery_address__isnull=False + ).order_by('-created_at') + self.fields['address_from_history'].queryset = Address.objects.filter( + order__in=customer_orders + ).distinct().order_by('-created_at') + class OrderItemForm(forms.ModelForm): """Форма для позиции заказа""" diff --git a/myproject/orders/management/commands/create_demo_orders.py b/myproject/orders/management/commands/create_demo_orders.py index 277173e..1299cb3 100644 --- a/myproject/orders/management/commands/create_demo_orders.py +++ b/myproject/orders/management/commands/create_demo_orders.py @@ -10,8 +10,8 @@ from datetime import datetime, timedelta import random from decimal import Decimal -from orders.models import Order, OrderItem -from customers.models import Customer, Address +from orders.models import Order, OrderItem, Address +from customers.models import Customer from shops.models import Shop from products.models import Product diff --git a/myproject/orders/migrations/0003_remove_address_model.py b/myproject/orders/migrations/0003_remove_address_model.py new file mode 100644 index 0000000..d65a0b8 --- /dev/null +++ b/myproject/orders/migrations/0003_remove_address_model.py @@ -0,0 +1,46 @@ +# Generated by Django 5.0.10 on 2025-11-10 23:09 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orders', '0002_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Address', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('recipient_name', models.CharField(help_text='Имя человека, которому будет доставлен заказ', max_length=200, verbose_name='Имя получателя')), + ('recipient_phone', models.CharField(blank=True, help_text='Контактный телефон получателя для уточнения адреса', max_length=20, null=True, verbose_name='Телефон получателя')), + ('street', models.CharField(max_length=255, verbose_name='Улица')), + ('building_number', models.CharField(max_length=20, verbose_name='Номер здания')), + ('apartment_number', models.CharField(blank=True, max_length=20, null=True, verbose_name='Номер квартиры/офиса')), + ('district', models.CharField(blank=True, help_text='Район в Минске для удобства доставки', max_length=100, null=True, verbose_name='Район')), + ('delivery_instructions', models.TextField(blank=True, help_text='Дополнительные инструкции для курьера (домофон, подъезд и т.д.)', null=True, verbose_name='Инструкции для доставки')), + ('confirm_address_with_recipient', models.BooleanField(default=False, help_text='Курьер должен уточнить адрес у получателя перед доставкой', verbose_name='Уточнить адрес у получателя')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')), + ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')), + ], + options={ + 'verbose_name': 'Адрес доставки', + 'verbose_name_plural': 'Адреса доставки', + 'ordering': ['-created_at'], + 'indexes': [models.Index(fields=['district'], name='orders_addr_distric_fd94e9_idx')], + }, + ), + migrations.AlterField( + model_name='historicalorder', + name='delivery_address', + field=models.ForeignKey(blank=True, db_constraint=False, help_text='Обязательно для курьерской доставки', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='orders.address', verbose_name='Адрес доставки'), + ), + migrations.AlterField( + model_name='order', + name='delivery_address', + field=models.OneToOneField(blank=True, help_text='Обязательно для курьерской доставки', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='order', to='orders.address', verbose_name='Адрес доставки'), + ), + ] diff --git a/myproject/orders/migrations/0004_remove_address_orders_addr_distric_fd94e9_idx_and_more.py b/myproject/orders/migrations/0004_remove_address_orders_addr_distric_fd94e9_idx_and_more.py new file mode 100644 index 0000000..eb58a27 --- /dev/null +++ b/myproject/orders/migrations/0004_remove_address_orders_addr_distric_fd94e9_idx_and_more.py @@ -0,0 +1,45 @@ +# Generated by Django 5.0.10 on 2025-11-10 23:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orders', '0003_remove_address_model'), + ] + + operations = [ + migrations.RemoveIndex( + model_name='address', + name='orders_addr_distric_fd94e9_idx', + ), + migrations.RemoveField( + model_name='address', + name='district', + ), + migrations.AddField( + model_name='address', + name='entrance', + field=models.CharField(blank=True, help_text='Номер подъезда/входа', max_length=20, null=True, verbose_name='Подъезд'), + ), + migrations.AddField( + model_name='address', + name='floor', + field=models.CharField(blank=True, max_length=20, null=True, verbose_name='Этаж'), + ), + migrations.AddField( + model_name='address', + name='intercom_code', + field=models.CharField(blank=True, help_text='Код домофона для входа в здание', max_length=100, null=True, verbose_name='Код домофона'), + ), + migrations.AlterField( + model_name='address', + name='delivery_instructions', + field=models.TextField(blank=True, help_text='Дополнительные инструкции для курьера', null=True, verbose_name='Инструкции для доставки'), + ), + migrations.AddIndex( + model_name='address', + index=models.Index(fields=['created_at'], name='orders_addr_created_98ad97_idx'), + ), + ] diff --git a/myproject/orders/models.py b/myproject/orders/models.py index 38df793..26be6fe 100644 --- a/myproject/orders/models.py +++ b/myproject/orders/models.py @@ -1,12 +1,120 @@ from django.db import models from django.core.exceptions import ValidationError from accounts.models import CustomUser -from customers.models import Customer, Address +from customers.models import Customer from products.models import Product, ProductKit from shops.models import Shop from simple_history.models import HistoricalRecords +class Address(models.Model): + """ + Модель адреса доставки для заказа цветочного магазина в Минске. + Адрес принадлежит конкретному заказу доставки. + """ + # Информация о получателе + recipient_name = models.CharField( + max_length=200, + verbose_name="Имя получателя", + help_text="Имя человека, которому будет доставлен заказ" + ) + + recipient_phone = models.CharField( + max_length=20, + blank=True, + null=True, + verbose_name="Телефон получателя", + help_text="Контактный телефон получателя для уточнения адреса" + ) + + street = models.CharField( + max_length=255, + verbose_name="Улица" + ) + + building_number = models.CharField( + max_length=20, + verbose_name="Номер здания" + ) + + apartment_number = models.CharField( + max_length=20, + blank=True, + null=True, + verbose_name="Номер квартиры/офиса" + ) + + entrance = models.CharField( + max_length=20, + blank=True, + null=True, + verbose_name="Подъезд", + help_text="Номер подъезда/входа" + ) + + floor = models.CharField( + max_length=20, + blank=True, + null=True, + verbose_name="Этаж" + ) + + intercom_code = models.CharField( + max_length=100, + blank=True, + null=True, + verbose_name="Код домофона", + help_text="Код домофона для входа в здание" + ) + + # Дополнительная информация для доставки + delivery_instructions = models.TextField( + blank=True, + null=True, + verbose_name="Инструкции для доставки", + help_text="Дополнительные инструкции для курьера" + ) + + confirm_address_with_recipient = models.BooleanField( + default=False, + verbose_name="Уточнить адрес у получателя", + help_text="Курьер должен уточнить адрес у получателя перед доставкой" + ) + + # Временные метки + created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания") + updated_at = models.DateTimeField(auto_now=True, verbose_name="Дата обновления") + + class Meta: + verbose_name = "Адрес доставки" + verbose_name_plural = "Адреса доставки" + indexes = [ + models.Index(fields=['created_at']), + ] + ordering = ['-created_at'] + + def __str__(self): + address_line = f"{self.street}, {self.building_number}" + if self.apartment_number: + address_line += f", кв/офис {self.apartment_number}" + return f"{self.recipient_name} - {address_line}" + + @property + def full_address(self): + """Полный адрес для доставки""" + address = f"{self.street}, {self.building_number}" + if self.apartment_number: + address += f", кв/офис {self.apartment_number}" + details = [] + if self.entrance: + details.append(f"подъезд {self.entrance}") + if self.floor: + details.append(f"этаж {self.floor}") + if details: + address += f" ({', '.join(details)})" + return address + + class Order(models.Model): """ Заказ клиента для доставки цветов. @@ -34,12 +142,12 @@ class Order(models.Model): ) # Адрес доставки (для курьерской доставки) - delivery_address = models.ForeignKey( + delivery_address = models.OneToOneField( Address, - on_delete=models.PROTECT, + on_delete=models.CASCADE, null=True, blank=True, - related_name='orders', + related_name='order', verbose_name="Адрес доставки", help_text="Обязательно для курьерской доставки" ) diff --git a/myproject/orders/services/address_service.py b/myproject/orders/services/address_service.py new file mode 100644 index 0000000..b9e6c5d --- /dev/null +++ b/myproject/orders/services/address_service.py @@ -0,0 +1,134 @@ +""" +Сервис для работы с адресами заказов. +Содержит логику создания, обновления и управления адресами доставки. +""" +from ..models import Order, Address + + +class AddressService: + """Сервис для управления адресами доставки в заказах""" + + @staticmethod + def create_address_from_form_data(form_data): + """ + Создает объект Address из данных формы. + + Args: + form_data (dict): Словарь с данными из формы + - address_street + - address_building_number + - address_apartment_number (опционально) + - address_entrance (опционально) + - address_floor (опционально) + - address_intercom_code (опционально) + - address_delivery_instructions (опционально) + + Returns: + Address: Новый объект адреса (не сохраненный в БД) + """ + address = Address( + recipient_name=form_data.get('recipient_name', ''), + recipient_phone=form_data.get('recipient_phone', ''), + street=form_data.get('address_street', ''), + building_number=form_data.get('address_building_number', ''), + apartment_number=form_data.get('address_apartment_number', ''), + entrance=form_data.get('address_entrance', ''), + floor=form_data.get('address_floor', ''), + intercom_code=form_data.get('address_intercom_code', ''), + delivery_instructions=form_data.get('address_delivery_instructions', ''), + confirm_address_with_recipient=form_data.get('address_confirm_with_recipient', False), + ) + return address + + @staticmethod + def process_address_from_form(order, form_data): + """ + Обрабатывает адрес из данных формы заказа. + Создает новый Address или использует существующий в зависимости от режима. + + Args: + order (Order): Объект заказа + form_data (dict): Все данные из формы + + Returns: + Address or None: Адрес для привязки к заказу или None + """ + address_mode = form_data.get('address_mode') + + # Если режим "без адреса" - возвращаем None + if address_mode == 'empty': + return None + + # Если режим "выбрать из истории" - возвращаем выбранный адрес + if address_mode == 'history': + address_id = form_data.get('address_from_history') + if address_id: + try: + return Address.objects.get(pk=address_id) + except Address.DoesNotExist: + return None + + # Если режим "ввести новый адрес" + if address_mode == 'new': + # Проверяем обязательные поля + street = form_data.get('address_street', '').strip() + building_number = form_data.get('address_building_number', '').strip() + + if not street or not building_number: + # Если обязательные поля не заполнены, возвращаем None + return None + + # Создаем новый адрес + address = AddressService.create_address_from_form_data(form_data) + return address + + return None + + @staticmethod + def get_customer_address_history(customer): + """ + Получает список адресов из истории заказов клиента. + + Args: + customer (Customer): Клиент + + Returns: + QuerySet: Адреса, отсортированные по дате создания (новые первыми) + """ + customer_orders = Order.objects.filter( + customer=customer, + delivery_address__isnull=False + ).order_by('-created_at') + + addresses = Address.objects.filter( + order__in=customer_orders + ).distinct().order_by('-created_at') + + return addresses + + @staticmethod + def format_address_for_display(address): + """ + Форматирует адрес для отображения в списке. + + Args: + address (Address): Объект адреса + + Returns: + str: Форматированная строка адреса + """ + address_line = f"{address.street}, {address.building_number}" + + if address.apartment_number: + address_line += f", кв. {address.apartment_number}" + + details = [] + if address.entrance: + details.append(f"подъезд {address.entrance}") + if address.floor: + details.append(f"этаж {address.floor}") + + if details: + address_line += f" ({', '.join(details)})" + + return address_line diff --git a/myproject/orders/services/draft_service.py b/myproject/orders/services/draft_service.py index 79e9fd7..a5d972a 100644 --- a/myproject/orders/services/draft_service.py +++ b/myproject/orders/services/draft_service.py @@ -9,8 +9,9 @@ from decimal import Decimal import decimal from datetime import datetime, date, time -from ..models import Order, OrderItem +from ..models import Order, OrderItem, Address from products.models import Product, ProductKit +from .address_service import AddressService class DraftOrderService: @@ -87,7 +88,6 @@ class DraftOrderService: # ForeignKey поля требуют специальной обработки fk_fields = { 'customer': 'customers.Customer', - 'delivery_address': 'customers.Address', 'pickup_shop': 'shops.Shop', } @@ -113,6 +113,31 @@ class DraftOrderService: except Model.DoesNotExist: pass # Игнорируем несуществующие объекты + # === Обработка адреса доставки === + # Новая логика с выбором режима адреса + if 'address_mode' in data: + address = AddressService.process_address_from_form(order, data) + if address: + # Если адрес не существует в БД, сохраняем его + if not address.pk: + address.save() + order.delivery_address = address + else: + # Если режим "без адреса", удаляем существующий адрес + if order.delivery_address: + old_address = order.delivery_address + order.delivery_address = None + # Удаляем старый адрес если он больше не используется + if old_address and not old_address.order: + old_address.delete() + elif 'delivery_address' in data and data['delivery_address']: + # Старая логика для совместимости (если передается delivery_address напрямую) + try: + address = Address.objects.get(pk=data['delivery_address']) + order.delivery_address = address + except Address.DoesNotExist: + pass + # Обрабатываем простые поля for field in simple_fields: if field in data: diff --git a/myproject/orders/static/orders/js/draft-creator.js b/myproject/orders/static/orders/js/draft-creator.js index 80a7aff..2d3d261 100644 --- a/myproject/orders/static/orders/js/draft-creator.js +++ b/myproject/orders/static/orders/js/draft-creator.js @@ -241,6 +241,71 @@ data.pickup_shop = parseInt(pickupShopField.value); } + // Новая логика выбора адреса + const addressModeField = form.querySelector('input[name="address_mode"]:checked'); + if (addressModeField) { + data.address_mode = addressModeField.value; + + if (addressModeField.value === 'history') { + const addressFromHistoryField = form.querySelector('select[name="address_from_history"]'); + if (addressFromHistoryField && addressFromHistoryField.value) { + data.address_from_history = parseInt(addressFromHistoryField.value); + } + } else if (addressModeField.value === 'new') { + // Собираем поля нового адреса + const addressStreetField = form.querySelector('input[name="address_street"]'); + if (addressStreetField && addressStreetField.value) { + data.address_street = addressStreetField.value; + } + + const addressBuildingField = form.querySelector('input[name="address_building_number"]'); + if (addressBuildingField && addressBuildingField.value) { + data.address_building_number = addressBuildingField.value; + } + + const addressApartmentField = form.querySelector('input[name="address_apartment_number"]'); + if (addressApartmentField && addressApartmentField.value) { + data.address_apartment_number = addressApartmentField.value; + } + + const addressEntranceField = form.querySelector('input[name="address_entrance"]'); + if (addressEntranceField && addressEntranceField.value) { + data.address_entrance = addressEntranceField.value; + } + + const addressFloorField = form.querySelector('input[name="address_floor"]'); + if (addressFloorField && addressFloorField.value) { + data.address_floor = addressFloorField.value; + } + + const addressIntercomField = form.querySelector('input[name="address_intercom_code"]'); + if (addressIntercomField && addressIntercomField.value) { + data.address_intercom_code = addressIntercomField.value; + } + + const addressInstructionsField = form.querySelector('textarea[name="address_delivery_instructions"]'); + if (addressInstructionsField && addressInstructionsField.value) { + data.address_delivery_instructions = addressInstructionsField.value; + } + + const addressConfirmField = form.querySelector('input[name="address_confirm_with_recipient"]'); + if (addressConfirmField) { + data.address_confirm_with_recipient = addressConfirmField.checked; + } + } + } + + // Поля получателя + const recipientNameField = form.querySelector('input[name="recipient_name"]'); + if (recipientNameField && recipientNameField.value) { + data.recipient_name = recipientNameField.value; + } + + const recipientPhoneField = form.querySelector('input[name="recipient_phone"]'); + if (recipientPhoneField && recipientPhoneField.value) { + data.recipient_phone = recipientPhoneField.value; + } + // Собираем позиции заказа const items = collectOrderItems(); if (items.length > 0) { diff --git a/myproject/orders/templates/orders/order_form.html b/myproject/orders/templates/orders/order_form.html index 557929d..49b39c0 100644 --- a/myproject/orders/templates/orders/order_form.html +++ b/myproject/orders/templates/orders/order_form.html @@ -16,6 +16,23 @@ pointer-events: none; } + /* Явное управление видимостью режимов адреса */ + #address-history-mode { + display: none !important; + } + + #address-new-mode { + display: none !important; + } + + #address-history-mode.visible { + display: block !important; + } + + #address-new-mode.visible { + display: block !important; + } + /* Стили для поиска клиента */ .customer-option { padding: 5px 0; @@ -193,17 +210,141 @@