feat(pos): добавлен полноценный интерфейс скидок в модальное окно продажи

- Добавлен API endpoint /pos/api/discounts/available/ для получения списка доступных скидок
- Добавлен метод DiscountApplier.apply_manual_discount() для применения ручных скидок
- Обновлен POS checkout для обработки manual_discount_id
- Расширена секция скидок в модальном окне:
  * Отображение автоматических скидок (read-only)
  * Dropdown для выбора скидки вручную
  * Подробная детализация: подитог, общая скидка, скидки на позиции
  * Поле промокода с иконкой
- Увеличен размер модального окна и изменено соотношение колонок (5/7)
- Убрана вертикальная прокрутка из модального окна

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-11 01:59:43 +03:00
parent 6313b8f6e7
commit 42d8c34e8c
5 changed files with 389 additions and 30 deletions

View File

@@ -214,6 +214,69 @@ class DiscountApplier:
# Пересчитываем
order.calculate_total()
@staticmethod
@transaction.atomic
def apply_manual_discount(order, discount, user=None):
"""
Применить скидку вручную к заказу.
Args:
order: Order
discount: Discount
user: CustomUser (применивший скидку)
Returns:
dict: {
'success': bool,
'discount_amount': Decimal,
'error': str
}
"""
from discounts.models import DiscountApplication
# Проверяем scope скидки
if discount.scope != 'order':
return {'success': False, 'error': 'Эта скидка не применяется к заказу'}
# Проверяем мин. сумму
if discount.min_order_amount and order.subtotal < discount.min_order_amount:
return {'success': False, 'error': f'Мин. сумма заказа: {discount.min_order_amount} руб.'}
# Удаляем предыдущую скидку на заказ
DiscountApplier._remove_order_discount_only(order)
# Рассчитываем сумму
discount_amount = discount.calculate_discount_amount(Decimal(order.subtotal))
# Применяем к заказу
order.applied_discount = discount
order.discount_amount = discount_amount
order.save(update_fields=['applied_discount', 'discount_amount'])
# Пересчитываем total_amount
order.calculate_total()
# Создаем запись о применении
DiscountApplication.objects.create(
order=order,
discount=discount,
target='order',
base_amount=order.subtotal,
discount_amount=discount_amount,
final_amount=order.subtotal - discount_amount,
customer=order.customer,
applied_by=user
)
# Увеличиваем счетчик использований скидки
discount.current_usage_count += 1
discount.save(update_fields=['current_usage_count'])
return {
'success': True,
'discount_amount': discount_amount
}
@staticmethod
def _remove_order_discount_only(order):
"""