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>
207 lines
9.2 KiB
Python
207 lines
9.2 KiB
Python
"""
|
||
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 сигналы!'))
|