Реализована система управления стоимостью доставки и исправлен баг выбора клиента

Изменения:

1. **Стоимость доставки (автоматическая/ручная)**:
   - Добавлено поле `is_custom_delivery_cost` в модель Order для отслеживания ручной стоимости
   - Создан сервис DeliveryCostCalculator для автоматического расчета стоимости доставки
   - Реализована логика: бесплатная доставка от 100 руб., иначе 15 руб.
   - В форме поле "Ручная стоимость доставки" - если заполнено, используется ручная стоимость, если пустое - автоматический расчет
   - Добавлены методы Order.get_delivery_cost(), set_delivery_cost(), reset_delivery_cost(), recalculate_delivery_cost()

2. **Исправление бага выбора клиента в Select2**:
   - Удален избыточный обработчик события select2:opening, который блокировал клики
   - Добавлены проверки на наличие e.params.data в обработчиках select2:selecting и select2:select
   - Теперь выбор клиента работает корректно, создается черновик заказа и происходит редирект

3. **Опциональные поля адреса**:
   - Поля recipient_name, street, building_number в модели Address сделаны необязательными (blank=True, null=True)
   - Обновлены методы __str__ и full_address для безопасной работы с None значениями

4. **UI улучшения**:
   - Удалена звездочка обязательности с полей адреса
   - Добавлена подсказка под полем ручной стоимости доставки о правилах автоматического расчета

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-11 15:48:50 +03:00
parent 6c207e9451
commit 885ac839e2
5 changed files with 351 additions and 60 deletions

View File

@@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-
"""
Сервис для расчета стоимости доставки.
Содержит расширяемую логику вычисления на основе различных условий.
"""
from decimal import Decimal
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from orders.models import Order
class DeliveryCostCalculator:
"""
Калькулятор стоимости доставки.
Применяет различные правила для автоматического расчета.
"""
# Константы для правил расчета
FREE_DELIVERY_THRESHOLD = Decimal('100.00') # Бесплатная доставка от суммы
BASE_DELIVERY_COST = Decimal('15.00') # Базовая стоимость доставки
MIN_DELIVERY_COST = Decimal('0.00') # Минимальная стоимость
@classmethod
def calculate(cls, order: 'Order') -> Decimal:
"""
Рассчитывает стоимость доставки на основе условий заказа.
Args:
order: Заказ для расчета
Returns:
Decimal: Рассчитанная стоимость доставки
"""
# Самовывоз - доставка бесплатная
if not order.is_delivery:
return cls.MIN_DELIVERY_COST
# Рассчитываем сумму товаров
items_total = sum(
item.get_total_price()
for item in order.items.all()
)
# Применяем правила расчета
cost = cls._apply_calculation_rules(order, items_total)
return cost
@classmethod
def _apply_calculation_rules(cls, order: 'Order', items_total: Decimal) -> Decimal:
"""
Применяет правила расчета стоимости доставки.
Этот метод легко расширить для добавления новых правил.
Args:
order: Заказ
items_total: Сумма товаров в заказе
Returns:
Decimal: Стоимость доставки
"""
# Правило 1: Бесплатная доставка при заказе от определенной суммы
if items_total >= cls.FREE_DELIVERY_THRESHOLD:
return cls.MIN_DELIVERY_COST
# Правило 2: Базовая стоимость доставки
cost = cls.BASE_DELIVERY_COST
# Правило 3: Можно добавить расчет по адресу
# if order.delivery_address:
# cost += cls._calculate_distance_cost(order.delivery_address)
# Правило 4: Можно добавить надбавку за срочность
# if cls._is_urgent_delivery(order):
# cost *= Decimal('1.5')
return cost
@classmethod
def _calculate_distance_cost(cls, address) -> Decimal:
"""
Рассчитывает надбавку за расстояние.
Placeholder для будущей реализации с геокодингом.
"""
# TODO: Интеграция с картами для расчета расстояния
return Decimal('0.00')
@classmethod
def _is_urgent_delivery(cls, order: 'Order') -> bool:
"""
Проверяет, является ли доставка срочной.
"""
# TODO: Логика определения срочности
return False