Initial commit: Django inventory system
This commit is contained in:
0
myproject/products/management/commands/__init__.py
Normal file
0
myproject/products/management/commands/__init__.py
Normal file
245
myproject/products/management/commands/demo_variants.py
Normal file
245
myproject/products/management/commands/demo_variants.py
Normal file
@@ -0,0 +1,245 @@
|
||||
"""
|
||||
Management команда для демонстрации работы системы вариантов товаров.
|
||||
|
||||
Использование:
|
||||
python manage.py demo_variants
|
||||
"""
|
||||
|
||||
from decimal import Decimal
|
||||
from django.core.management.base import BaseCommand
|
||||
from products.models import (
|
||||
Product, ProductKit, KitItem, ProductCategory,
|
||||
ProductVariantGroup, KitItemPriority
|
||||
)
|
||||
from products.utils.stock_manager import StockManager
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Демонстрация работы системы вариантов товаров'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
self.stdout.write("\n" + "="*60)
|
||||
self.stdout.write(self.style.SUCCESS("ДЕМОНСТРАЦИЯ СИСТЕМЫ ВАРИАНТОВ ТОВАРОВ"))
|
||||
self.stdout.write("="*60 + "\n")
|
||||
|
||||
# Создаём демо-данные
|
||||
group, rose_50, rose_60, rose_70 = self.create_variant_group()
|
||||
|
||||
# Создаём букеты
|
||||
premium_kit = self.create_premium_bouquet(group, rose_50, rose_60, rose_70)
|
||||
economy_kit = self.create_economy_bouquet(group, rose_50, rose_60, rose_70)
|
||||
|
||||
# Проверяем доступность
|
||||
self.check_availability(premium_kit, economy_kit)
|
||||
|
||||
# Получаем лучший товар
|
||||
self.show_best_product(premium_kit)
|
||||
|
||||
self.stdout.write("\n" + "="*60)
|
||||
self.stdout.write(self.style.SUCCESS("ДЕМОНСТРАЦИЯ ЗАВЕРШЕНА"))
|
||||
self.stdout.write("="*60 + "\n")
|
||||
|
||||
def create_variant_group(self):
|
||||
"""Создание группы вариантов"""
|
||||
self.stdout.write("\n" + "="*60)
|
||||
self.stdout.write(self.style.HTTP_INFO("ШАГ 1: Создание группы вариантов"))
|
||||
self.stdout.write("="*60)
|
||||
|
||||
# Создаём категорию
|
||||
category, _ = ProductCategory.objects.get_or_create(
|
||||
name="Цветы",
|
||||
defaults={'slug': 'cvety'}
|
||||
)
|
||||
|
||||
# Создаём товары - розы разной длины
|
||||
rose_50, _ = Product.objects.get_or_create(
|
||||
name="Роза Freedom 50см красная",
|
||||
defaults={
|
||||
'cost_price': Decimal('80.00'),
|
||||
'sale_price': Decimal('100.00'),
|
||||
'category': category
|
||||
}
|
||||
)
|
||||
|
||||
rose_60, _ = Product.objects.get_or_create(
|
||||
name="Роза Freedom 60см красная",
|
||||
defaults={
|
||||
'cost_price': Decimal('120.00'),
|
||||
'sale_price': Decimal('150.00'),
|
||||
'category': category
|
||||
}
|
||||
)
|
||||
|
||||
rose_70, _ = Product.objects.get_or_create(
|
||||
name="Роза Freedom 70см красная",
|
||||
defaults={
|
||||
'cost_price': Decimal('160.00'),
|
||||
'sale_price': Decimal('200.00'),
|
||||
'category': category
|
||||
}
|
||||
)
|
||||
|
||||
# Создаём группу вариантов
|
||||
group, created = ProductVariantGroup.objects.get_or_create(
|
||||
name="Роза красная Freedom",
|
||||
defaults={
|
||||
'description': 'Красная роза Freedom различной длины (50-70см)'
|
||||
}
|
||||
)
|
||||
|
||||
# Добавляем товары в группу
|
||||
rose_50.variant_groups.add(group)
|
||||
rose_60.variant_groups.add(group)
|
||||
rose_70.variant_groups.add(group)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(f"[OK] Создана группа: {group.name}"))
|
||||
self.stdout.write(f" Товаров в группе: {group.get_products_count()}")
|
||||
self.stdout.write(" Товары:")
|
||||
for product in group.products.all():
|
||||
self.stdout.write(f" - {product.name} ({product.sale_price} руб.)")
|
||||
|
||||
return group, rose_50, rose_60, rose_70
|
||||
|
||||
def create_premium_bouquet(self, group, rose_50, rose_60, rose_70):
|
||||
"""Создание премиум букета"""
|
||||
self.stdout.write("\n" + "="*60)
|
||||
self.stdout.write(self.style.HTTP_INFO("ШАГ 2: Создание премиум букета"))
|
||||
self.stdout.write("="*60)
|
||||
|
||||
# Создаём букет
|
||||
kit, _ = ProductKit.objects.get_or_create(
|
||||
name="Ранчо Виталия Премиум",
|
||||
defaults={
|
||||
'slug': 'rancho-vitaliya-premium',
|
||||
'pricing_method': 'from_sale_prices'
|
||||
}
|
||||
)
|
||||
|
||||
# Создаём позицию с группой вариантов
|
||||
kit_item, _ = KitItem.objects.get_or_create(
|
||||
kit=kit,
|
||||
variant_group=group,
|
||||
defaults={
|
||||
'quantity': Decimal('15.000'),
|
||||
'notes': 'Использовать самые длинные розы'
|
||||
}
|
||||
)
|
||||
|
||||
# Настраиваем приоритеты (для премиум букета - сначала длинные)
|
||||
KitItemPriority.objects.get_or_create(
|
||||
kit_item=kit_item,
|
||||
product=rose_70,
|
||||
defaults={'priority': 0}
|
||||
)
|
||||
KitItemPriority.objects.get_or_create(
|
||||
kit_item=kit_item,
|
||||
product=rose_60,
|
||||
defaults={'priority': 1}
|
||||
)
|
||||
KitItemPriority.objects.get_or_create(
|
||||
kit_item=kit_item,
|
||||
product=rose_50,
|
||||
defaults={'priority': 2}
|
||||
)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(f"[OK] Создан букет: {kit.name}"))
|
||||
self.stdout.write(f" Позиций: {kit.get_total_components_count()}")
|
||||
self.stdout.write(f" С вариантами: {kit.get_components_with_variants_count()}")
|
||||
self.stdout.write(f"\n Приоритеты для позиции '{kit_item.get_display_name()}':")
|
||||
for priority in kit_item.priorities.all().order_by('priority'):
|
||||
self.stdout.write(f" {priority.priority}. {priority.product.name} - {priority.product.sale_price} руб.")
|
||||
|
||||
return kit
|
||||
|
||||
def create_economy_bouquet(self, group, rose_50, rose_60, rose_70):
|
||||
"""Создание эконом букета"""
|
||||
self.stdout.write("\n" + "="*60)
|
||||
self.stdout.write(self.style.HTTP_INFO("ШАГ 3: Создание эконом букета"))
|
||||
self.stdout.write("="*60)
|
||||
|
||||
kit, _ = ProductKit.objects.get_or_create(
|
||||
name="Ранчо Виталия Эконом",
|
||||
defaults={
|
||||
'slug': 'rancho-vitaliya-econom',
|
||||
'pricing_method': 'from_sale_prices'
|
||||
}
|
||||
)
|
||||
|
||||
kit_item, _ = KitItem.objects.get_or_create(
|
||||
kit=kit,
|
||||
variant_group=group,
|
||||
defaults={
|
||||
'quantity': Decimal('15.000'),
|
||||
'notes': 'Эконом вариант'
|
||||
}
|
||||
)
|
||||
|
||||
# Для эконом букета - сначала короткие (дешевые)
|
||||
KitItemPriority.objects.get_or_create(
|
||||
kit_item=kit_item,
|
||||
product=rose_50,
|
||||
defaults={'priority': 0}
|
||||
)
|
||||
KitItemPriority.objects.get_or_create(
|
||||
kit_item=kit_item,
|
||||
product=rose_60,
|
||||
defaults={'priority': 1}
|
||||
)
|
||||
KitItemPriority.objects.get_or_create(
|
||||
kit_item=kit_item,
|
||||
product=rose_70,
|
||||
defaults={'priority': 2}
|
||||
)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(f"[OK] Создан букет: {kit.name}"))
|
||||
self.stdout.write(f"\n Приоритеты для позиции '{kit_item.get_display_name()}':")
|
||||
for priority in kit_item.priorities.all().order_by('priority'):
|
||||
self.stdout.write(f" {priority.priority}. {priority.product.name} - {priority.product.sale_price} руб.")
|
||||
|
||||
return kit
|
||||
|
||||
def check_availability(self, premium_kit, economy_kit):
|
||||
"""Проверка доступности"""
|
||||
self.stdout.write("\n" + "="*60)
|
||||
self.stdout.write(self.style.HTTP_INFO("ШАГ 4: Проверка доступности букетов"))
|
||||
self.stdout.write("="*60)
|
||||
|
||||
stock_manager = StockManager()
|
||||
|
||||
# Проверяем премиум букет
|
||||
self.stdout.write(f"\nПремиум букет: {premium_kit.name}")
|
||||
if premium_kit.check_availability(stock_manager):
|
||||
self.stdout.write(self.style.SUCCESS(" [OK] Доступен для сборки"))
|
||||
price = premium_kit.calculate_price_with_substitutions(stock_manager)
|
||||
self.stdout.write(f" Цена: {price} руб.")
|
||||
else:
|
||||
self.stdout.write(self.style.ERROR(" [ERROR] Недоступен"))
|
||||
|
||||
# Проверяем эконом букет
|
||||
self.stdout.write(f"\nЭконом букет: {economy_kit.name}")
|
||||
if economy_kit.check_availability(stock_manager):
|
||||
self.stdout.write(self.style.SUCCESS(" [OK] Доступен для сборки"))
|
||||
price = economy_kit.calculate_price_with_substitutions(stock_manager)
|
||||
self.stdout.write(f" Цена: {price} руб.")
|
||||
else:
|
||||
self.stdout.write(self.style.ERROR(" [ERROR] Недоступен"))
|
||||
|
||||
def show_best_product(self, kit):
|
||||
"""Показать лучший доступный товар"""
|
||||
self.stdout.write("\n" + "="*60)
|
||||
self.stdout.write(self.style.HTTP_INFO("ШАГ 5: Выбор лучшего доступного товара"))
|
||||
self.stdout.write("="*60)
|
||||
|
||||
stock_manager = StockManager()
|
||||
|
||||
for kit_item in kit.kit_items.all():
|
||||
self.stdout.write(f"\nПозиция: {kit_item.get_display_name()}")
|
||||
self.stdout.write(f"Количество: {kit_item.quantity}")
|
||||
|
||||
best_product = kit_item.get_best_available_product(stock_manager)
|
||||
if best_product:
|
||||
self.stdout.write(self.style.SUCCESS(f"[OK] Лучший доступный товар: {best_product.name}"))
|
||||
self.stdout.write(f" Цена: {best_product.sale_price} руб.")
|
||||
self.stdout.write(f" Стоимость позиции: {best_product.sale_price * kit_item.quantity} руб.")
|
||||
else:
|
||||
self.stdout.write(self.style.ERROR("[ERROR] Нет доступных товаров"))
|
||||
34
myproject/products/management/commands/fix_category_slugs.py
Normal file
34
myproject/products/management/commands/fix_category_slugs.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils.text import slugify
|
||||
from unidecode import unidecode
|
||||
from products.models import ProductCategory
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Fixes category slugs by converting Cyrillic to Latin transliteration'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
categories = ProductCategory.objects.all()
|
||||
fixed_count = 0
|
||||
|
||||
for category in categories:
|
||||
old_slug = category.slug
|
||||
# Generate new slug with Latin transliteration
|
||||
transliterated_name = unidecode(category.name)
|
||||
new_slug = slugify(transliterated_name)
|
||||
|
||||
if old_slug != new_slug:
|
||||
category.slug = new_slug
|
||||
category.save()
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
f'Fixed: "{category.name}" | {old_slug} -> {new_slug}'
|
||||
)
|
||||
)
|
||||
fixed_count += 1
|
||||
else:
|
||||
self.stdout.write(f'OK: "{category.name}" | {old_slug}')
|
||||
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(f'\nTotal fixed: {fixed_count} categories')
|
||||
)
|
||||
Reference in New Issue
Block a user