Add stock reservation auto-update system
- Add management command to recalculate quantity_reserved for all Stock records - Add signals to automatically update Stock when Reservation changes - Implement post_save signal for Reservation creation/updates - Implement post_delete signal for Reservation deletion - Both signals call Stock.refresh_from_batches() to recalculate quantities 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
"""
|
||||
Management команда для пересчета quantity_reserved для всех Stock записей
|
||||
Используется после добавления сигналов для Reservation
|
||||
"""
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import connection, transaction
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Пересчитывает quantity_reserved для всех Stock записей'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--schema',
|
||||
type=str,
|
||||
default='grach',
|
||||
help='Схема базы данных (tenant) для работы'
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
schema_name = options['schema']
|
||||
|
||||
self.stdout.write(f'[НАЧАЛО] Пересчет quantity_reserved для всех Stock записей в схеме {schema_name}...')
|
||||
|
||||
updated_count = 0
|
||||
reserved_count = 0
|
||||
|
||||
# Используем прямой SQL для пересчета quantity_reserved
|
||||
# Это обходит проблему с tenant routing в Django ORM
|
||||
with connection.cursor() as cursor, transaction.atomic():
|
||||
# Устанавливаем схему
|
||||
cursor.execute(f'SET search_path TO {schema_name}')
|
||||
|
||||
# Получаем общее количество Stock записей
|
||||
cursor.execute(f'SELECT COUNT(*) FROM {schema_name}.inventory_stock')
|
||||
total_count = cursor.fetchone()[0]
|
||||
|
||||
self.stdout.write(f'[INFO] Найдено Stock записей: {total_count}')
|
||||
|
||||
# Получаем все Stock записи с информацией о продукте
|
||||
cursor.execute(f"""
|
||||
SELECT
|
||||
s.id as stock_id,
|
||||
s.product_id,
|
||||
p.name as product_name,
|
||||
s.warehouse_id
|
||||
FROM {schema_name}.inventory_stock s
|
||||
JOIN {schema_name}.products_product p ON p.id = s.product_id
|
||||
""")
|
||||
stocks = cursor.fetchall()
|
||||
|
||||
# Обрабатываем каждый Stock
|
||||
for stock_id, product_id, product_name, warehouse_id in stocks:
|
||||
try:
|
||||
# Пересчитываем quantity_reserved и quantity_available
|
||||
# Логика из Stock.refresh_from_batches()
|
||||
|
||||
# 1. Считаем общее количество из активных партий
|
||||
cursor.execute(f"""
|
||||
SELECT COALESCE(SUM(quantity), 0)
|
||||
FROM {schema_name}.inventory_stockbatch
|
||||
WHERE product_id = %s
|
||||
AND warehouse_id = %s
|
||||
AND is_active = true
|
||||
""", [product_id, warehouse_id])
|
||||
total_qty = cursor.fetchone()[0]
|
||||
|
||||
# 2. Считаем зарезервированное количество
|
||||
cursor.execute(f"""
|
||||
SELECT COALESCE(SUM(quantity), 0)
|
||||
FROM {schema_name}.inventory_reservation
|
||||
WHERE product_id = %s
|
||||
AND warehouse_id = %s
|
||||
AND status = 'reserved'
|
||||
""", [product_id, warehouse_id])
|
||||
total_reserved = cursor.fetchone()[0]
|
||||
|
||||
# 3. Обновляем Stock
|
||||
cursor.execute(f"""
|
||||
UPDATE {schema_name}.inventory_stock
|
||||
SET quantity_available = %s,
|
||||
quantity_reserved = %s,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = %s
|
||||
""", [total_qty, total_reserved, stock_id])
|
||||
|
||||
updated_count += 1
|
||||
|
||||
if total_reserved > 0:
|
||||
reserved_count += 1
|
||||
self.stdout.write(
|
||||
f' [OK] Stock #{stock_id}: {product_name} - '
|
||||
f'зарезервировано: {total_reserved}'
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
self.stdout.write(
|
||||
self.style.ERROR(f' [ОШИБКА] Stock #{stock_id}: {str(e)}')
|
||||
)
|
||||
|
||||
self.stdout.write('\n' + '=' * 60)
|
||||
self.stdout.write(self.style.SUCCESS('[ЗАВЕРШЕНО] Результаты:'))
|
||||
self.stdout.write(f' Всего Stock записей: {total_count}')
|
||||
self.stdout.write(f' Обновлено записей: {updated_count}')
|
||||
self.stdout.write(f' Записей с резервами: {reserved_count}')
|
||||
self.stdout.write(self.style.SUCCESS('\n[OK] Все резервы пересчитаны!'))
|
||||
Reference in New Issue
Block a user