Files
octopus/myproject/orders/models/delivery.py
Andrey Smakotin 298d797286 Исправление проблем с сохранением адреса, получателя и даты доставки
- Исправлено: адрес теперь сохраняется для черновиков заказов
- Исправлено: получатель корректно предзаполняется при редактировании заказа
- Исправлено: адрес при редактировании отображается в режиме 'новый' для возможности редактирования
- Исправлено: дата доставки корректно предзаполняется при редактировании заказа
- Исправлено: при редактировании получателя обновляется существующий объект вместо создания нового
- Улучшена логика обработки Delivery для черновиков (создание с опциональными полями)
- Улучшена логика обновления получателя через загрузку заказа из БД с select_related
2025-12-25 00:30:27 +03:00

166 lines
6.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
from django.db import models
from django.core.exceptions import ValidationError
class Delivery(models.Model):
"""
Модель доставки заказа.
Один заказ имеет одну доставку.
"""
# Константы для типов доставки
DELIVERY_TYPE_COURIER = 'courier'
DELIVERY_TYPE_PICKUP = 'pickup'
DELIVERY_TYPE_CHOICES = [
(DELIVERY_TYPE_COURIER, 'Доставка курьером'),
(DELIVERY_TYPE_PICKUP, 'Самовывоз'),
]
# === Связи ===
order = models.OneToOneField(
'orders.Order',
on_delete=models.CASCADE,
related_name='delivery',
verbose_name='Заказ',
help_text='Заказ, к которому относится доставка'
)
# Адрес доставки (только для курьерской доставки)
address = models.ForeignKey(
'orders.Address',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='deliveries',
verbose_name='Адрес доставки',
help_text='Адрес для курьерской доставки. На один адрес может быть много доставок'
)
# Склад для самовывоза (только для самовывоза)
pickup_warehouse = models.ForeignKey(
'inventory.Warehouse',
on_delete=models.PROTECT,
null=True,
blank=True,
related_name='deliveries',
verbose_name='Склад самовывоза',
help_text='Склад для самовывоза заказа'
)
# === Основные поля ===
delivery_type = models.CharField(
max_length=20,
choices=DELIVERY_TYPE_CHOICES,
default=DELIVERY_TYPE_COURIER,
verbose_name='Способ доставки',
db_index=True
)
# Дата и время доставки
delivery_date = models.DateField(
verbose_name='Дата доставки',
help_text='Дата, когда должна быть выполнена доставка'
)
time_from = models.TimeField(
null=True,
blank=True,
verbose_name='Время доставки от',
help_text='Начальное время временного интервала доставки (необязательно)'
)
time_to = models.TimeField(
null=True,
blank=True,
verbose_name='Время доставки до',
help_text='Конечное время временного интервала доставки (необязательно)'
)
cost = models.DecimalField(
max_digits=10,
decimal_places=2,
default=0,
verbose_name='Стоимость доставки',
help_text='Стоимость доставки в рублях. 0 для бесплатной доставки/самовывоза'
)
# === Метаданные ===
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 = 'Доставки'
ordering = ['-created_at']
indexes = [
models.Index(fields=['delivery_type']),
models.Index(fields=['created_at']),
models.Index(fields=['delivery_date']),
models.Index(fields=['time_from']),
models.Index(fields=['time_to']),
]
def __str__(self):
"""Строковое представление доставки"""
type_display = self.get_delivery_type_display()
return f"{type_display} для заказа #{self.order.order_number}"
def clean(self):
"""Валидация модели"""
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:
raise ValidationError({
'address': 'Для курьерской доставки необходимо указать адрес'
})
if self.pickup_warehouse:
raise ValidationError({
'pickup_warehouse': 'Для курьерской доставки склад не указывается'
})
# Проверка: для самовывоза должен быть склад
if self.delivery_type == self.DELIVERY_TYPE_PICKUP:
if not self.pickup_warehouse:
raise ValidationError({
'pickup_warehouse': 'Для самовывоза необходимо указать склад'
})
if self.address:
raise ValidationError({
'address': 'Для самовывоза адрес не указывается'
})
# Проверка: время "до" должно быть позже времени "от"
if self.time_from and self.time_to and self.time_from >= self.time_to:
raise ValidationError({
'time_to': 'Время окончания доставки должно быть позже времени начала'
})
def save(self, *args, **kwargs):
"""Переопределение save для вызова валидации"""
self.full_clean()
super().save(*args, **kwargs)