Добавлена защита от повторного списания + команда исправления дубликатов
Проблема: Сигнал post_save срабатывает несколько раз, создавая дубликаты Sale для одного заказа. Решения: 1. Добавлена проверка Sale.objects.filter(order=instance).exists() перед созданием продаж (inventory/signals.py:74-75) 2. Создана management команда fix_duplicate_sales для исправления существующих дубликатов 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
76
myproject/orders/management/commands/fix_duplicate_sales.py
Normal file
76
myproject/orders/management/commands/fix_duplicate_sales.py
Normal file
@@ -0,0 +1,76 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import connection, transaction
|
||||
from orders.models import Order
|
||||
from inventory.models import Sale, SaleBatchAllocation, Stock, StockBatch, Warehouse
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Исправляет дубликаты продаж для указанного заказа'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('order_number', type=str, help='Номер заказа')
|
||||
parser.add_argument('--tenant', type=str, default='buba', help='Схема тенанта')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
order_number = options['order_number']
|
||||
tenant = options['tenant']
|
||||
|
||||
connection.set_schema(tenant)
|
||||
|
||||
try:
|
||||
order = Order.objects.get(order_number=order_number)
|
||||
except Order.DoesNotExist:
|
||||
self.stdout.write(self.style.ERROR(f'Заказ {order_number} не найден в тенанте {tenant}'))
|
||||
return
|
||||
|
||||
sales = Sale.objects.filter(order=order).order_by('date')
|
||||
|
||||
self.stdout.write(f"Заказ {order.order_number}: найдено {sales.count()} продаж")
|
||||
|
||||
if sales.count() <= 1:
|
||||
self.stdout.write(self.style.SUCCESS("Дубликатов нет, всё в порядке"))
|
||||
return
|
||||
|
||||
# Оставляем первую продажу, остальные удаляем
|
||||
first_sale = sales.first()
|
||||
duplicate_sales = sales.exclude(id=first_sale.id)
|
||||
|
||||
self.stdout.write(f"\nОставляем продажу ID {first_sale.id}")
|
||||
self.stdout.write(f"Удаляем {duplicate_sales.count()} дубликатов:")
|
||||
|
||||
with transaction.atomic():
|
||||
for sale in duplicate_sales:
|
||||
self.stdout.write(f" - Продажа ID {sale.id}: {sale.product.name} x {sale.quantity}")
|
||||
|
||||
# Получаем SaleBatchAllocation для восстановления товара
|
||||
allocations = SaleBatchAllocation.objects.filter(sale=sale)
|
||||
|
||||
# Восстанавливаем товар в партиях
|
||||
for alloc in allocations:
|
||||
batch = alloc.batch
|
||||
self.stdout.write(f" Восстанавливаем партию ID {batch.id}: +{alloc.quantity}")
|
||||
batch.quantity += alloc.quantity
|
||||
batch.is_active = True
|
||||
batch.save()
|
||||
|
||||
# Удаляем продажу (каскадно удалятся и SaleBatchAllocation)
|
||||
sale.delete()
|
||||
|
||||
# Обновляем Stock
|
||||
for item in order.items.all():
|
||||
product = item.product or item.product_kit
|
||||
if product:
|
||||
warehouse = order.pickup_warehouse or Warehouse.objects.filter(is_active=True).first()
|
||||
if warehouse:
|
||||
stock, _ = Stock.objects.get_or_create(product=product, warehouse=warehouse)
|
||||
stock.refresh_from_batches()
|
||||
self.stdout.write(f"\nStock обновлен для {product.name}:")
|
||||
self.stdout.write(f" quantity_available: {stock.quantity_available}")
|
||||
self.stdout.write(f" quantity_reserved: {stock.quantity_reserved}")
|
||||
self.stdout.write(f" quantity_free: {stock.quantity_free}")
|
||||
|
||||
self.stdout.write(self.style.SUCCESS("\n✅ Дубликаты удалены, товар восстановлен на складе"))
|
||||
|
||||
# Проверяем результат
|
||||
sales_after = Sale.objects.filter(order=order)
|
||||
self.stdout.write(f"\nПосле исправления: {sales_after.count()} продаж")
|
||||
Reference in New Issue
Block a user