diff --git a/myproject/orders/forms.py b/myproject/orders/forms.py index df2c7c6..a4efacf 100644 --- a/myproject/orders/forms.py +++ b/myproject/orders/forms.py @@ -303,6 +303,8 @@ class OrderForm(forms.ModelForm): # Инициализируем поля доставки из существующей Delivery if self.instance.pk and hasattr(self.instance, 'delivery'): delivery = self.instance.delivery + + # Устанавливаем значения через fields.initial для правильной работы виджетов self.fields['delivery_type'].initial = delivery.delivery_type self.fields['delivery_date'].initial = delivery.delivery_date self.fields['time_from'].initial = delivery.time_from @@ -310,6 +312,19 @@ class OrderForm(forms.ModelForm): self.fields['pickup_warehouse'].initial = delivery.pickup_warehouse self.fields['delivery_cost'].initial = delivery.cost + # Также устанавливаем через self.initial для совместимости + self.initial['delivery_type'] = delivery.delivery_type + self.initial['delivery_date'] = delivery.delivery_date + self.initial['time_from'] = delivery.time_from + self.initial['time_to'] = delivery.time_to + self.initial['pickup_warehouse'] = delivery.pickup_warehouse + self.initial['delivery_cost'] = delivery.cost + + # Для DateInput с type='date' нужно установить значение в формате YYYY-MM-DD + if delivery.delivery_date: + # Устанавливаем значение напрямую в виджете + self.fields['delivery_date'].widget.attrs['value'] = delivery.delivery_date.strftime('%Y-%m-%d') + # Если выбран самовывоз, но склад не указан - выбираем склад по умолчанию if delivery.delivery_type == Delivery.DELIVERY_TYPE_PICKUP and not delivery.pickup_warehouse: default_warehouse = Warehouse.objects.filter( @@ -322,38 +337,26 @@ class OrderForm(forms.ModelForm): # Инициализируем поля адреса, если есть адрес доставки if delivery.address: - # Проверяем, есть ли этот адрес в истории клиента + # При редактировании всегда используем режим "новый", чтобы можно было редактировать адрес + # Инициализируем queryset для истории адресов (для выбора из истории, если нужно) if self.instance.customer: customer_addresses = Address.objects.filter( deliveries__order__customer=self.instance.customer + ).exclude( + deliveries__order=self.instance # Исключаем адрес текущего заказа ).distinct() - if delivery.address in customer_addresses: - # Адрес есть в истории - используем режим "история" - self.fields['address_mode'].initial = 'history' - self.fields['address_from_history'].queryset = customer_addresses - self.fields['address_from_history'].initial = delivery.address.pk - else: - # Адреса нет в истории - используем режим "новый" и заполняем поля - self.fields['address_mode'].initial = 'new' - self.fields['address_street'].initial = delivery.address.street - self.fields['address_building_number'].initial = delivery.address.building_number - self.fields['address_apartment_number'].initial = delivery.address.apartment_number - self.fields['address_entrance'].initial = delivery.address.entrance - self.fields['address_floor'].initial = delivery.address.floor - self.fields['address_intercom_code'].initial = delivery.address.intercom_code - self.fields['address_delivery_instructions'].initial = delivery.address.delivery_instructions - self.fields['address_confirm_with_recipient'].initial = delivery.address.confirm_address_with_recipient - else: - # Нет клиента - просто заполняем поля - self.fields['address_mode'].initial = 'new' - self.fields['address_street'].initial = delivery.address.street - self.fields['address_building_number'].initial = delivery.address.building_number - self.fields['address_apartment_number'].initial = delivery.address.apartment_number - self.fields['address_entrance'].initial = delivery.address.entrance - self.fields['address_floor'].initial = delivery.address.floor - self.fields['address_intercom_code'].initial = delivery.address.intercom_code - self.fields['address_delivery_instructions'].initial = delivery.address.delivery_instructions - self.fields['address_confirm_with_recipient'].initial = delivery.address.confirm_address_with_recipient + self.fields['address_from_history'].queryset = customer_addresses + + # Всегда используем режим "новый" для редактирования, чтобы можно было редактировать + self.initial['address_mode'] = 'new' + self.initial['address_street'] = delivery.address.street + self.initial['address_building_number'] = delivery.address.building_number + self.initial['address_apartment_number'] = delivery.address.apartment_number + self.initial['address_entrance'] = delivery.address.entrance + self.initial['address_floor'] = delivery.address.floor + self.initial['address_intercom_code'] = delivery.address.intercom_code + self.initial['address_delivery_instructions'] = delivery.address.delivery_instructions + self.initial['address_confirm_with_recipient'] = delivery.address.confirm_address_with_recipient def clean(self): """Валидация формы заказа, включая обязательные поля доставки""" diff --git a/myproject/orders/models/delivery.py b/myproject/orders/models/delivery.py index c8a5454..6fa408b 100644 --- a/myproject/orders/models/delivery.py +++ b/myproject/orders/models/delivery.py @@ -121,6 +121,16 @@ class Delivery(models.Model): """Валидация модели""" super().clean() + # Для черновиков пропускаем строгую валидацию + if self.order and self.order.status and hasattr(self.order.status, 'code') and self.order.status.code == 'draft': + # Для черновиков только проверяем время, если оно указано + if self.time_from and self.time_to and self.time_from >= self.time_to: + raise ValidationError({ + 'time_to': 'Время окончания доставки должно быть позже времени начала' + }) + return + + # Для не-черновиков полная валидация # Проверка: для курьерской доставки должен быть адрес if self.delivery_type == self.DELIVERY_TYPE_COURIER: if not self.address: diff --git a/myproject/orders/services/address_service.py b/myproject/orders/services/address_service.py index f737de7..85d47b6 100644 --- a/myproject/orders/services/address_service.py +++ b/myproject/orders/services/address_service.py @@ -95,7 +95,22 @@ class AddressService: if not name or not phone: return None - # Проверяем, есть ли уже такой получатель в БД + # Если у заказа уже есть получатель - обновляем его вместо создания нового + # Загружаем заказ из БД, чтобы получить актуального получателя + if order.pk: + try: + # Order уже импортирован в начале файла + db_order = Order.objects.select_related('recipient').get(pk=order.pk) + if db_order.recipient: + # Обновляем существующего получателя + db_order.recipient.name = name + db_order.recipient.phone = phone + # Сохранять будем в views.py, здесь просто возвращаем объект + return db_order.recipient + except Order.DoesNotExist: + pass + + # Проверяем, есть ли уже такой получатель в БД (по имени и телефону) existing_recipient = Recipient.objects.filter( name=name, phone=phone diff --git a/myproject/orders/templates/orders/order_form.html b/myproject/orders/templates/orders/order_form.html index 0208a3b..989598d 100644 --- a/myproject/orders/templates/orders/order_form.html +++ b/myproject/orders/templates/orders/order_form.html @@ -585,25 +585,6 @@ - -
-
-
Дополнительно
-
-
-
- {{ form.is_anonymous }} - -
-
- - {{ form.special_instructions }} -
-
-
-
@@ -613,7 +594,10 @@
- {{ form.other_recipient }} + @@ -683,6 +667,25 @@
+ + +
+
+
Дополнительно
+
+
+
+ {{ form.is_anonymous }} + +
+
+ + {{ form.special_instructions }} +
+
+
@@ -935,6 +938,22 @@ document.addEventListener('DOMContentLoaded', function() { + @@ -1099,8 +1118,17 @@ document.addEventListener('DOMContentLoaded', function() { // Обработчики событий if (otherRecipientCheckbox) { otherRecipientCheckbox.addEventListener('change', toggleOtherRecipientBlock); - // Инициализация при загрузке - toggleOtherRecipientBlock(); + // Инициализация при загрузке - проверяем checked состояние + // Используем DOMContentLoaded чтобы убедиться, что DOM полностью загружен + // Но также проверяем сразу, если DOM уже загружен + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', function() { + toggleOtherRecipientBlock(); + }); + } else { + // DOM уже загружен + toggleOtherRecipientBlock(); + } } recipientSourceRadios.forEach(radio => { diff --git a/myproject/orders/views.py b/myproject/orders/views.py index 3956316..cfc7773 100644 --- a/myproject/orders/views.py +++ b/myproject/orders/views.py @@ -121,30 +121,53 @@ def order_create(request): # Проверяем, является ли заказ черновиком is_draft = order.status and order.status.code == 'draft' - # Создаем Delivery (обязательно, кроме черновиков) - if not is_draft: - # Получаем данные из формы (уже провалидированы) - delivery_type = form.cleaned_data.get('delivery_type') - delivery_date = form.cleaned_data.get('delivery_date') - time_from = form.cleaned_data.get('time_from') - time_to = form.cleaned_data.get('time_to') - delivery_cost = form.cleaned_data.get('delivery_cost', Decimal('0')) - pickup_warehouse = form.cleaned_data.get('pickup_warehouse') - - # Проверяем наличие обязательных полей (время необязательно) + # Получаем данные из формы (уже провалидированы) + delivery_type = form.cleaned_data.get('delivery_type') + delivery_date = form.cleaned_data.get('delivery_date') + time_from = form.cleaned_data.get('time_from') + time_to = form.cleaned_data.get('time_to') + delivery_cost = form.cleaned_data.get('delivery_cost', Decimal('0')) + pickup_warehouse = form.cleaned_data.get('pickup_warehouse') + + # Обрабатываем адрес для курьерской доставки (даже для черновиков, если указан) + address = None + if delivery_type == Delivery.DELIVERY_TYPE_COURIER: + # Для курьерской доставки обрабатываем адрес, если он указан + address = AddressService.process_address_from_form(order, form.cleaned_data) + if address and not address.pk: + address.save() + + # Создаем или обновляем Delivery + # Для черновиков создаем Delivery без обязательных проверок, чтобы сохранить адрес + if is_draft: + # Для черновиков создаем Delivery, если есть хотя бы адрес или данные доставки + if address or delivery_type or pickup_warehouse: + # Для черновиков используем значения по умолчанию, если не указаны + from django.utils import timezone + draft_delivery_type = delivery_type or Delivery.DELIVERY_TYPE_COURIER + draft_delivery_date = delivery_date or timezone.now().date() + + Delivery.objects.update_or_create( + order=order, + defaults={ + 'delivery_type': draft_delivery_type, + 'delivery_date': draft_delivery_date, + 'time_from': time_from, + 'time_to': time_to, + 'address': address, + 'pickup_warehouse': pickup_warehouse, + 'cost': delivery_cost if delivery_cost else Decimal('0') + } + ) + else: + # Для не-черновиков проверяем обязательные поля if not delivery_type or not delivery_date: raise ValidationError('Необходимо указать способ доставки и дату доставки') - # Обрабатываем адрес для курьерской доставки - address = None - if delivery_type == Delivery.DELIVERY_TYPE_COURIER: # Для курьерской доставки нужен адрес - address = AddressService.process_address_from_form(order, form.cleaned_data) if not address: raise ValidationError('Для курьерской доставки необходимо указать адрес') - if not address.pk: - address.save() elif delivery_type == Delivery.DELIVERY_TYPE_PICKUP: # Для самовывоза нужен склад if not pickup_warehouse: @@ -259,9 +282,8 @@ def order_update(request, order_number): # Обрабатываем получателя recipient = AddressService.process_recipient_from_form(order, form.cleaned_data) if recipient: - # Если получатель не существует в БД, сохраняем его - if not recipient.pk: - recipient.save() + # Сохраняем получателя: если новый - создаем, если существующий - обновляем + recipient.save() # Django автоматически определит create или update order.recipient = recipient else: # Если покупатель является получателем @@ -274,30 +296,56 @@ def order_update(request, order_number): # Проверяем, является ли заказ черновиком is_draft = order.status and order.status.code == 'draft' - # Создаем или обновляем Delivery (обязательно, кроме черновиков) - if not is_draft: - # Получаем данные из формы (уже провалидированы) - delivery_type = form.cleaned_data.get('delivery_type') - delivery_date = form.cleaned_data.get('delivery_date') - time_from = form.cleaned_data.get('time_from') - time_to = form.cleaned_data.get('time_to') - delivery_cost = form.cleaned_data.get('delivery_cost', Decimal('0')) - pickup_warehouse = form.cleaned_data.get('pickup_warehouse') - - # Проверяем наличие обязательных полей (время необязательно) + # Получаем данные из формы (уже провалидированы) + delivery_type = form.cleaned_data.get('delivery_type') + delivery_date = form.cleaned_data.get('delivery_date') + time_from = form.cleaned_data.get('time_from') + time_to = form.cleaned_data.get('time_to') + delivery_cost = form.cleaned_data.get('delivery_cost', Decimal('0')) + pickup_warehouse = form.cleaned_data.get('pickup_warehouse') + + # Обрабатываем адрес для курьерской доставки (даже для черновиков, если указан) + address = None + if delivery_type == Delivery.DELIVERY_TYPE_COURIER: + # Для курьерской доставки обрабатываем адрес, если он указан + address = AddressService.process_address_from_form(order, form.cleaned_data) + if address and not address.pk: + address.save() + + # Создаем или обновляем Delivery + # Для черновиков создаем Delivery без обязательных проверок, чтобы сохранить адрес + if is_draft: + # Для черновиков создаем Delivery, если есть хотя бы адрес или данные доставки + if address or delivery_type or pickup_warehouse: + # Для черновиков используем значения по умолчанию, если не указаны + from django.utils import timezone + draft_delivery_type = delivery_type or Delivery.DELIVERY_TYPE_COURIER + draft_delivery_date = delivery_date or timezone.now().date() + + Delivery.objects.update_or_create( + order=order, + defaults={ + 'delivery_type': draft_delivery_type, + 'delivery_date': draft_delivery_date, + 'time_from': time_from, + 'time_to': time_to, + 'address': address, + 'pickup_warehouse': pickup_warehouse, + 'cost': delivery_cost if delivery_cost else Decimal('0') + } + ) + elif hasattr(order, 'delivery'): + # Если заказ стал черновиком и нет данных доставки, удаляем Delivery + order.delivery.delete() + else: + # Для не-черновиков проверяем обязательные поля if not delivery_type or not delivery_date: raise ValidationError('Необходимо указать способ доставки и дату доставки') - # Обрабатываем адрес для курьерской доставки - address = None - if delivery_type == Delivery.DELIVERY_TYPE_COURIER: # Для курьерской доставки нужен адрес - address = AddressService.process_address_from_form(order, form.cleaned_data) if not address: raise ValidationError('Для курьерской доставки необходимо указать адрес') - if not address.pk: - address.save() elif delivery_type == Delivery.DELIVERY_TYPE_PICKUP: # Для самовывоза нужен склад if not pickup_warehouse: @@ -316,9 +364,6 @@ def order_update(request, order_number): 'cost': delivery_cost if delivery_cost else Decimal('0') } ) - elif hasattr(order, 'delivery'): - # Если заказ стал черновиком, удаляем Delivery - order.delivery.delete() # Пересчитываем итоговую стоимость order.calculate_total()