diff --git a/myproject/inventory/signals.py b/myproject/inventory/signals.py index e50a7d5..606f5fe 100644 --- a/myproject/inventory/signals.py +++ b/myproject/inventory/signals.py @@ -4,6 +4,7 @@ Подключаются при создании, изменении и удалении заказов. """ +import threading from django.db.models.signals import post_save, pre_delete, post_delete, pre_save from django.db.models import Q from django.db import transaction @@ -19,6 +20,26 @@ from inventory.services import SaleProcessor from inventory.services.batch_manager import StockBatchManager # InventoryProcessor больше не используется в сигналах - обработка вызывается явно через view +# ============================================================================ +# Thread-local storage для временных флагов управления сигналами +# ============================================================================ +_skip_sale_creation = threading.local() + + +def skip_sale_creation(): + """Установить флаг для пропуска создания Sale в сигнале.""" + _skip_sale_creation.value = True + + +def reset_sale_creation(): + """Сбросить флаг пропуска создания Sale.""" + _skip_sale_creation.value = False + + +def is_skip_sale_creation(): + """Проверить, установлен ли флаг пропуска создания Sale.""" + return getattr(_skip_sale_creation, 'value', False) + # ============================================================================ # pre_save сигнал для сохранения предыдущего статуса Order @@ -278,10 +299,22 @@ def create_sale_on_order_completion(sender, instance, created, **kwargs): 3. Для каждого товара создаем Sale (автоматический FIFO-список) 4. ТОЛЬКО после успешного создания Sale обновляем резервы на 'converted_to_sale' 5. Обновляем флаг is_returned + + ПРИМЕЧАНИЕ: Если у Order установлен атрибут skip_sale_creation=True, + создание Sale пропускается (используется в POS для создания Sale после применения скидок). """ import logging logger = logging.getLogger(__name__) + # === ПРОВЕРКА: Пропуск создания Sale по флагу === + # Используется в POS checkout, где Sale создаётся явно после применения скидок + if is_skip_sale_creation(): + logger.info( + f"ℹ️ Заказ {instance.order_number}: skip_sale_creation=True (thread-local), " + f"пропускаем автоматическое создание Sale" + ) + return + if created: return # Только для обновлений diff --git a/myproject/pos/views.py b/myproject/pos/views.py index e8b2535..3402d4c 100644 --- a/myproject/pos/views.py +++ b/myproject/pos/views.py @@ -15,6 +15,7 @@ import logging from products.models import Product, ProductCategory, ProductKit, KitItem from inventory.models import Showcase, Reservation, Warehouse, Stock from inventory.services import ShowcaseManager +from inventory.signals import skip_sale_creation, reset_sale_creation logger = logging.getLogger(__name__) @@ -1526,13 +1527,17 @@ def pos_checkout(request): # Атомарная операция with db_transaction.atomic(): + # ВАЖНО: Устанавливаем флаг для пропуска автоматического создания Sale в сигнале. + # Sale будет создан ЯВНО после применения всех скидок. + skip_sale_creation() + # 1. Создаём заказ с текущей датой и временем в локальном часовом поясе (Europe/Minsk) from django.utils import timezone as tz from orders.models import Delivery now_utc = tz.now() # Текущее время в UTC now_local = tz.localtime(now_utc) # Конвертируем в локальный часовой пояс (Europe/Minsk) current_time = now_local.time() # Извлекаем время в минском часовом поясе - + order = Order.objects.create( customer=customer, status=completed_status, # Сразу "Выполнен" @@ -1714,6 +1719,11 @@ def pos_checkout(request): cart_key = f'pos:cart:{request.user.id}:{warehouse_id}' cache.delete(cart_key) + # 7. Явно создаём Sale после применения всех скидок + # Сбрасываем флаг пропуска и вызываем save() для активации сигнала + reset_sale_creation() + order.save() # Триггерит сигнал create_sale_on_order_completion + return JsonResponse({ 'success': True, 'order_number': order.order_number,