Обновления и новые функции: изменение шаблона клиента, обновление сигналов инвентаря, добавление снимков наборов и элементов заказа, обновление моделей заказов и продуктов

This commit is contained in:
2025-12-18 00:14:24 +03:00
parent 56725e8092
commit 7b32cdcebf
9 changed files with 547 additions and 87 deletions

View File

@@ -0,0 +1,163 @@
"""
Снапшоты комплектов для сохранения истории заказов.
При добавлении комплекта (ProductKit) в заказ создается снимок его состояния,
чтобы изменения в комплекте не влияли на историю заказов.
"""
from django.db import models
from decimal import Decimal
class KitSnapshot(models.Model):
"""
Снимок комплекта на момент заказа.
Сохраняет название, цены и состав комплекта.
"""
# Связь с оригинальным комплектом (для аналитики)
original_kit = models.ForeignKey(
'products.ProductKit',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='snapshots',
verbose_name="Оригинальный комплект",
help_text="Ссылка на комплект, с которого создан снимок"
)
# Копия основных данных комплекта
name = models.CharField(max_length=200, verbose_name="Название")
sku = models.CharField(max_length=100, blank=True, verbose_name="Артикул")
description = models.TextField(blank=True, verbose_name="Описание")
# Цены на момент заказа
base_price = models.DecimalField(
max_digits=10,
decimal_places=2,
default=Decimal('0'),
verbose_name="Базовая цена"
)
price = models.DecimalField(
max_digits=10,
decimal_places=2,
default=Decimal('0'),
verbose_name="Итоговая цена"
)
sale_price = models.DecimalField(
max_digits=10,
decimal_places=2,
null=True,
blank=True,
verbose_name="Цена со скидкой"
)
# Корректировки цены
price_adjustment_type = models.CharField(
max_length=20,
default='none',
verbose_name="Тип корректировки"
)
price_adjustment_value = models.DecimalField(
max_digits=10,
decimal_places=2,
default=Decimal('0'),
verbose_name="Значение корректировки"
)
is_temporary = models.BooleanField(default=False, verbose_name="Временный комплект")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")
class Meta:
verbose_name = "Снимок комплекта"
verbose_name_plural = "Снимки комплектов"
ordering = ['-created_at']
indexes = [
models.Index(fields=['original_kit']),
models.Index(fields=['created_at']),
]
def __str__(self):
date_str = self.created_at.strftime('%d.%m.%Y %H:%M') if self.created_at else ''
return f"Снимок: {self.name} ({date_str})"
@property
def actual_price(self):
"""Финальная цена (sale_price или price)"""
if self.sale_price:
return self.sale_price
return self.price
def get_total_components_count(self):
"""Количество компонентов в комплекте"""
return self.items.count()
class KitItemSnapshot(models.Model):
"""
Снимок компонента комплекта.
Сохраняет информацию о товаре и его количестве в комплекте.
"""
kit_snapshot = models.ForeignKey(
KitSnapshot,
on_delete=models.CASCADE,
related_name='items',
verbose_name="Снимок комплекта"
)
# Ссылка на оригинальный товар (для резервирования)
original_product = models.ForeignKey(
'products.Product',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='kit_item_snapshots',
verbose_name="Оригинальный товар",
help_text="Ссылка на товар для резервирования на складе"
)
# Данные о товаре
product_name = models.CharField(
max_length=200,
blank=True,
verbose_name="Название товара"
)
product_sku = models.CharField(
max_length=100,
blank=True,
verbose_name="Артикул товара"
)
product_price = models.DecimalField(
max_digits=10,
decimal_places=2,
default=Decimal('0'),
verbose_name="Цена товара"
)
# Если был выбран из группы вариантов
variant_group_name = models.CharField(
max_length=200,
blank=True,
verbose_name="Группа вариантов"
)
quantity = models.DecimalField(
max_digits=10,
decimal_places=3,
verbose_name="Количество"
)
class Meta:
verbose_name = "Снимок компонента"
verbose_name_plural = "Снимки компонентов"
indexes = [
models.Index(fields=['kit_snapshot']),
]
def __str__(self):
name = self.product_name or self.variant_group_name or "Неизвестный товар"
return f"{name} x{self.quantity}"
@property
def total_price(self):
"""Стоимость компонента (цена * количество)"""
return self.product_price * self.quantity