Files
octopus/myproject/orders/management/commands/create_demo_orders.py
Andrey Smakotin fcc7f2263d Fix product reservation system for demo orders
PROBLEM ANALYSIS:
- SQL script created orders bypassing Django ORM
- Django signals (post_save) didn't trigger
- No reservations were created automatically
- Found 51 orders with 102 items and 0 reservations

SOLUTION IMPLEMENTED:

1. Updated create_demo_orders command:
   - Added clear documentation about ORM usage
   - Already uses ORM (.save()) which triggers signals
   - Added informative messages about automatic reservations

2. Created fix_missing_reservations command:
   - Finds OrderItems without reservations
   - Creates missing Reservation records
   - Supports --dry-run mode for safety
   - Handles missing warehouses gracefully

3. Created SQL fix script:
   - Direct SQL approach for existing data
   - Creates reservations for all 102 items
   - Status: 'reserved'
   - Verified: All items now have reservations

4. Added verification scripts:
   - check_orders.py: Shows orders/items/reservations count
   - run_fix_reservations.py: Executes SQL fix

RESULTS:
- ✓ 102 reservations created for existing orders
- ✓ Future orders will use ORM and create reservations automatically
- ✓ System now works correctly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 00:04:55 +03:00

207 lines
9.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Management команда для создания демо-заказов на разные даты
ВАЖНО: Создает заказы через Django ORM, что автоматически активирует
сигналы резервирования товаров!
"""
from django.core.management.base import BaseCommand
from django.utils import timezone
from django.db import connection
from datetime import datetime, timedelta
import random
from decimal import Decimal
from orders.models import Order, OrderItem
from customers.models import Customer, Address
from shops.models import Shop
from products.models import Product
class Command(BaseCommand):
help = 'Создает демо-заказы через ORM (с автоматическим резервированием товаров)'
def add_arguments(self, parser):
parser.add_argument(
'--count',
type=int,
default=25,
help='Количество заказов для создания (по умолчанию: 25)'
)
parser.add_argument(
'--schema',
type=str,
default='grach',
help='Схема базы данных (tenant) для создания заказов'
)
def handle(self, *args, **options):
count = options['count']
schema_name = options['schema']
# Устанавливаем схему для работы с tenant
with connection.cursor() as cursor:
cursor.execute(f'SET search_path TO {schema_name}')
self.stdout.write(f'[НАЧАЛО] Создание {count} демо-заказов в схеме {schema_name}...')
self.stdout.write('[INFO] Заказы создаются через ORM - резервы товаров будут созданы автоматически!')
# Проверяем наличие необходимых данных
customers = list(Customer.objects.all())
if not customers:
self.stdout.write(self.style.ERROR('Нет клиентов в базе! Создайте хотя бы одного клиента.'))
return
products = list(Product.objects.all())
if not products:
self.stdout.write(self.style.ERROR('Нет товаров в базе! Создайте хотя бы один товар.'))
return
addresses = list(Address.objects.all())
shops = list(Shop.objects.all())
if not addresses and not shops:
self.stdout.write(self.style.ERROR('Нет ни адресов, ни магазинов! Создайте хотя бы что-то одно.'))
return
# Статусы и их вероятности
statuses = [
('new', 0.15),
('confirmed', 0.25),
('in_assembly', 0.20),
('in_delivery', 0.15),
('delivered', 0.20),
('cancelled', 0.05),
]
payment_statuses = [
('unpaid', 0.30),
('partial', 0.20),
('paid', 0.50),
]
payment_methods = [
'cash_to_courier',
'card_to_courier',
'online',
'bank_transfer',
]
# Генерируем даты в диапазоне ±15 дней от сегодня
today = datetime.now().date()
created_count = 0
for i in range(count):
try:
# Случайная дата доставки
days_offset = random.randint(-15, 15)
delivery_date = today + timedelta(days=days_offset)
# Выбираем клиента
customer = random.choice(customers)
# Выбираем тип доставки
is_delivery = random.choice([True, False]) if addresses and shops else bool(addresses)
# Создаем заказ
order = Order()
order.customer = customer
order.is_delivery = is_delivery
# Устанавливаем адрес или магазин
if is_delivery and addresses:
# Для доставки выбираем адрес клиента или случайный
customer_addresses = list(customer.addresses.all())
if customer_addresses:
order.delivery_address = random.choice(customer_addresses)
else:
order.delivery_address = random.choice(addresses)
order.delivery_cost = Decimal(random.randint(200, 500))
elif shops:
order.pickup_shop = random.choice(shops)
order.delivery_cost = Decimal(0)
# Дата и время
order.delivery_date = delivery_date
if random.random() > 0.3: # 70% заказов с указанным временем
start_hour = random.randint(9, 18)
order.delivery_time_start = f"{start_hour:02d}:00:00"
order.delivery_time_end = f"{start_hour + 2:02d}:00:00"
# Статус
status_choices = [s[0] for s in statuses]
status_weights = [s[1] for s in statuses]
order.status = random.choices(status_choices, weights=status_weights)[0]
# Способ оплаты
order.payment_method = random.choice(payment_methods)
# Дополнительная информация
if random.random() > 0.7: # 30% - подарок другому человеку
order.customer_is_recipient = False
order.recipient_name = f"Получатель {i+1}"
order.recipient_phone = f"+7{random.randint(9000000000, 9999999999)}"
if random.random() > 0.8: # 20% анонимных
order.is_anonymous = True
if random.random() > 0.5: # 50% с комментариями
comments = [
"Позвонить за час до доставки",
"Доставить точно в указанное время",
"Не звонить в дверь, только по телефону",
"Упаковать покрасивее",
"Приложить открытку",
]
order.special_instructions = random.choice(comments)
# Сохраняем заказ (чтобы получить ID)
order.save()
# Добавляем товары в заказ
items_count = random.randint(1, 4)
order_products = random.sample(products, min(items_count, len(products)))
items_total = Decimal(0)
for product in order_products:
item = OrderItem()
item.order = order
item.product = product
item.quantity = random.randint(1, 3)
item.price = product.price
item.save()
items_total += item.get_total_price()
# Рассчитываем итоговую сумму
order.total_amount = items_total + order.delivery_cost
# Скидка (20% заказов)
if random.random() > 0.8:
order.discount_amount = Decimal(random.randint(100, 500))
order.total_amount -= order.discount_amount
# Статус оплаты
payment_status_choices = [s[0] for s in payment_statuses]
payment_status_weights = [s[1] for s in payment_statuses]
order.payment_status = random.choices(payment_status_choices, weights=payment_status_weights)[0]
if order.payment_status == 'paid':
order.amount_paid = order.total_amount
order.is_paid = True
elif order.payment_status == 'partial':
order.amount_paid = order.total_amount * Decimal(random.uniform(0.2, 0.8))
order.is_paid = False
else:
order.amount_paid = Decimal(0)
order.is_paid = False
order.save()
created_count += 1
self.stdout.write(f' [OK] Заказ #{order.order_number} на {delivery_date} (товаров: {len(order_products)})')
except Exception as e:
self.stdout.write(self.style.ERROR(f'[ОШИБКА] Заказ {i+1}: {str(e)}'))
self.stdout.write(self.style.SUCCESS(f'\n[ЗАВЕРШЕНО] Успешно создано {created_count} заказов!'))
self.stdout.write(f'Даты доставки: от {today - timedelta(days=15)} до {today + timedelta(days=15)}')
self.stdout.write(self.style.SUCCESS('\n[ВАЖНО] Резервы товаров созданы автоматически через Django сигналы!'))