From e3bab2252e8f77d418113828e6a3bd9c84d1d01c Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Fri, 7 Nov 2025 23:49:06 +0300 Subject: [PATCH] Add demo orders creation scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Created management command for generating demo orders - Added SQL script to create 25 orders with random dates (±15 days) - Added Python runner script for executing SQL - Demo orders include varied statuses, payment methods, and delivery types - Orders distributed across different dates for testing date filter 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- myproject/create_demo_orders.sql | 186 ++++++++++++++++ myproject/orders/management/__init__.py | 1 + .../orders/management/commands/__init__.py | 1 + .../management/commands/create_demo_orders.py | 202 ++++++++++++++++++ myproject/run_demo_orders.py | 24 +++ 5 files changed, 414 insertions(+) create mode 100644 myproject/create_demo_orders.sql create mode 100644 myproject/orders/management/__init__.py create mode 100644 myproject/orders/management/commands/__init__.py create mode 100644 myproject/orders/management/commands/create_demo_orders.py create mode 100644 myproject/run_demo_orders.py diff --git a/myproject/create_demo_orders.sql b/myproject/create_demo_orders.sql new file mode 100644 index 0000000..fec1f2e --- /dev/null +++ b/myproject/create_demo_orders.sql @@ -0,0 +1,186 @@ +-- Создание демо-заказов для схемы grach +SET search_path TO grach; + +-- Создаем 25 заказов с разными датами (от -15 до +15 дней от сегодня) +DO $$ +DECLARE + customer_ids INT[]; + product_ids INT[]; + address_ids INT[]; + shop_ids INT[]; + i INT; + random_customer_id INT; + random_product_id INT; + random_address_id INT; + random_shop_id INT; + is_delivery_flag BOOLEAN; + delivery_date_val DATE; + status_val VARCHAR(20); + payment_status_val VARCHAR(20); + payment_method_val VARCHAR(20); + order_id INT; + items_total DECIMAL(10,2); + delivery_cost_val DECIMAL(10,2); + total_amount_val DECIMAL(10,2); +BEGIN + -- Получаем существующие ID + SELECT ARRAY_AGG(id) INTO customer_ids FROM grach.customers_customer; + SELECT ARRAY_AGG(id) INTO product_ids FROM grach.products_product; + SELECT ARRAY_AGG(id) INTO address_ids FROM grach.customers_address; + SELECT ARRAY_AGG(id) INTO shop_ids FROM grach.shops_shop; + + -- Проверяем наличие данных + IF customer_ids IS NULL OR array_length(customer_ids, 1) = 0 THEN + RAISE EXCEPTION 'Нет клиентов в базе!'; + END IF; + + IF product_ids IS NULL OR array_length(product_ids, 1) = 0 THEN + RAISE EXCEPTION 'Нет товаров в базе!'; + END IF; + + -- Создаем 25 заказов + FOR i IN 1..25 LOOP + -- Случайные значения + random_customer_id := customer_ids[1 + floor(random() * array_length(customer_ids, 1))::int]; + is_delivery_flag := (random() > 0.5); + delivery_date_val := CURRENT_DATE + (floor(random() * 31) - 15)::int; + + -- Случайный статус + CASE floor(random() * 6)::int + WHEN 0 THEN status_val := 'new'; + WHEN 1 THEN status_val := 'confirmed'; + WHEN 2 THEN status_val := 'in_assembly'; + WHEN 3 THEN status_val := 'in_delivery'; + WHEN 4 THEN status_val := 'delivered'; + ELSE status_val := 'cancelled'; + END CASE; + + -- Случайный статус оплаты + CASE floor(random() * 3)::int + WHEN 0 THEN payment_status_val := 'unpaid'; + WHEN 1 THEN payment_status_val := 'partial'; + ELSE payment_status_val := 'paid'; + END CASE; + + -- Случайный способ оплаты + CASE floor(random() * 4)::int + WHEN 0 THEN payment_method_val := 'cash_to_courier'; + WHEN 1 THEN payment_method_val := 'card_to_courier'; + WHEN 2 THEN payment_method_val := 'online'; + ELSE payment_method_val := 'bank_transfer'; + END CASE; + + -- Стоимость доставки + IF is_delivery_flag THEN + delivery_cost_val := 200 + floor(random() * 300)::int; + ELSE + delivery_cost_val := 0; + END IF; + + -- Создаем заказ + INSERT INTO grach.orders_order ( + customer_id, + order_number, + is_delivery, + delivery_address_id, + pickup_shop_id, + delivery_date, + delivery_time_start, + delivery_time_end, + delivery_cost, + status, + payment_method, + is_paid, + total_amount, + discount_amount, + amount_paid, + payment_status, + customer_is_recipient, + recipient_name, + recipient_phone, + is_anonymous, + special_instructions, + created_at, + updated_at, + modified_by_id + ) VALUES ( + random_customer_id, + 'ORD-' || to_char(CURRENT_DATE, 'YYYYMMDD') || '-' || substring(md5(random()::text) from 1 for 4), + is_delivery_flag, + CASE WHEN is_delivery_flag AND address_ids IS NOT NULL THEN address_ids[1 + floor(random() * array_length(address_ids, 1))::int] ELSE NULL END, + CASE WHEN NOT is_delivery_flag AND shop_ids IS NOT NULL THEN shop_ids[1 + floor(random() * array_length(shop_ids, 1))::int] ELSE NULL END, + delivery_date_val, + CASE WHEN random() > 0.3 THEN ((9 + floor(random() * 10)::int)::text || ':00:00')::time ELSE NULL END, + CASE WHEN random() > 0.3 THEN ((11 + floor(random() * 8)::int)::text || ':00:00')::time ELSE NULL END, + delivery_cost_val, + status_val, + payment_method_val, + (payment_status_val = 'paid'), + 1000, -- Временное значение, пересчитаем позже + CASE WHEN random() > 0.8 THEN (100 + floor(random() * 400)::int) ELSE 0 END, + 0, -- Временное значение + payment_status_val, + (random() > 0.7), + CASE WHEN random() > 0.7 THEN 'Получатель ' || i ELSE NULL END, + CASE WHEN random() > 0.7 THEN '+79' || lpad(floor(random() * 1000000000)::text, 9, '0') ELSE NULL END, + (random() > 0.8), + CASE WHEN random() > 0.5 THEN + CASE floor(random() * 5)::int + WHEN 0 THEN 'Позвонить за час до доставки' + WHEN 1 THEN 'Доставить точно в указанное время' + WHEN 2 THEN 'Не звонить в дверь' + WHEN 3 THEN 'Упаковать покрасивее' + ELSE 'Приложить открытку' + END + ELSE NULL END, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP, + NULL + ) RETURNING id INTO order_id; + + -- Добавляем 1-3 товара в заказ + items_total := 0; + FOR j IN 1..(1 + floor(random() * 3)::int) LOOP + random_product_id := product_ids[1 + floor(random() * array_length(product_ids, 1))::int]; + + -- Получаем цену товара и добавляем позицию + INSERT INTO grach.orders_orderitem ( + order_id, + product_id, + product_kit_id, + quantity, + price, + is_custom_price, + created_at + ) + SELECT + order_id, + random_product_id, + NULL, + 1 + floor(random() * 3)::int, + price, + FALSE, + CURRENT_TIMESTAMP + FROM grach.products_product + WHERE id = random_product_id + RETURNING (quantity * price) INTO STRICT total_amount_val; + + items_total := items_total + total_amount_val; + END LOOP; + + -- Обновляем итоговую сумму заказа + UPDATE grach.orders_order + SET + total_amount = items_total + delivery_cost - discount_amount, + amount_paid = CASE + WHEN payment_status = 'paid' THEN items_total + delivery_cost - discount_amount + WHEN payment_status = 'partial' THEN (items_total + delivery_cost - discount_amount) * (0.2 + random() * 0.6) + ELSE 0 + END + WHERE id = order_id; + + RAISE NOTICE 'Создан заказ % на дату %', order_id, delivery_date_val; + END LOOP; + + RAISE NOTICE 'Успешно создано 25 заказов!'; +END $$; diff --git a/myproject/orders/management/__init__.py b/myproject/orders/management/__init__.py new file mode 100644 index 0000000..e5999a7 --- /dev/null +++ b/myproject/orders/management/__init__.py @@ -0,0 +1 @@ +# Management commands for orders app diff --git a/myproject/orders/management/commands/__init__.py b/myproject/orders/management/commands/__init__.py new file mode 100644 index 0000000..2c1c7c1 --- /dev/null +++ b/myproject/orders/management/commands/__init__.py @@ -0,0 +1 @@ +# Management commands diff --git a/myproject/orders/management/commands/create_demo_orders.py b/myproject/orders/management/commands/create_demo_orders.py new file mode 100644 index 0000000..18feee3 --- /dev/null +++ b/myproject/orders/management/commands/create_demo_orders.py @@ -0,0 +1,202 @@ +""" +Management команда для создания демо-заказов на разные даты +""" +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 = 'Создает 20-30 демо-заказов на разные даты' + + 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'Начинаем создание демо-заказов в схеме {schema_name}...') + + # Проверяем наличие необходимых данных + 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' Создан заказ #{order.order_number} на {delivery_date}') + + 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)}') diff --git a/myproject/run_demo_orders.py b/myproject/run_demo_orders.py new file mode 100644 index 0000000..8b08c32 --- /dev/null +++ b/myproject/run_demo_orders.py @@ -0,0 +1,24 @@ +""" +Скрипт для создания демо-заказов напрямую через SQL +""" +import os +import django + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings') +django.setup() + +from django.db import connection + +# Читаем SQL скрипт +with open('create_demo_orders.sql', 'r', encoding='utf-8') as f: + sql = f.read() + +# Выполняем SQL +with connection.cursor() as cursor: + try: + cursor.execute(sql) + print("[OK] SQL script executed successfully!") + print("[OK] 25 demo orders created!") + except Exception as e: + print(f"[ERROR] {e}") + raise