Исправлен race condition в списании партий товара
- Добавлен параметр lock в get_batches_for_fifo() для блокировки строк - Используется select_for_update() в write_off_by_fifo() для предотвращения параллельной перезаписи quantity при одновременном списании из одной партии - Защита от потери данных при параллельном завершении заказов
This commit is contained in:
@@ -20,7 +20,7 @@ class StockBatchManager:
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_batches_for_fifo(product, warehouse):
|
||||
def get_batches_for_fifo(product, warehouse, lock=False):
|
||||
"""
|
||||
Получить все активные партии товара на складе для FIFO списания.
|
||||
Возвращает ВСЕ партии с quantity > 0, отсортированные по created_at.
|
||||
@@ -29,17 +29,25 @@ class StockBatchManager:
|
||||
Args:
|
||||
product: объект Product
|
||||
warehouse: объект Warehouse
|
||||
lock: bool - использовать ли select_for_update() для блокировки строк
|
||||
(защита от race condition при параллельном списании)
|
||||
|
||||
Returns:
|
||||
QuerySet отсортированных партий
|
||||
"""
|
||||
return StockBatch.objects.filter(
|
||||
queryset = StockBatch.objects.filter(
|
||||
product=product,
|
||||
warehouse=warehouse,
|
||||
is_active=True,
|
||||
quantity__gt=0 # Только партии с остатком
|
||||
).order_by('created_at') # FIFO: старые первыми
|
||||
|
||||
if lock:
|
||||
# Блокируем строки для предотвращения race condition
|
||||
queryset = queryset.select_for_update()
|
||||
|
||||
return queryset
|
||||
|
||||
@staticmethod
|
||||
def create_batch(product, warehouse, quantity, cost_price):
|
||||
"""
|
||||
@@ -127,8 +135,10 @@ class StockBatchManager:
|
||||
|
||||
total_reserved = reservation_filter.aggregate(total=Sum('quantity_base'))['total'] or Decimal('0')
|
||||
|
||||
# Получаем партии по FIFO
|
||||
batches = StockBatchManager.get_batches_for_fifo(product, warehouse)
|
||||
# Получаем партии по FIFO с блокировкой строк
|
||||
# Используем select_for_update() для предотвращения race condition
|
||||
# при параллельном списании из одной партии
|
||||
batches = StockBatchManager.get_batches_for_fifo(product, warehouse, lock=True)
|
||||
|
||||
# Проходим партии, списывая товар
|
||||
# Если есть exclude_transformation, сначала списываем из зарезервированного товара трансформации
|
||||
|
||||
Reference in New Issue
Block a user