# Generated for the initial product-centric commerce analytics schema. import uuid from decimal import Decimal import django.core.validators import django.db.models.deletion import django.db.models.functions.text from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [] operations = [ migrations.CreateModel( name="SourceSystem", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ( "code", models.SlugField(max_length=64, unique=True, verbose_name="Код"), ), ("name", models.CharField(max_length=128, verbose_name="Название")), ( "is_active", models.BooleanField(default=True, verbose_name="Активен"), ), ( "metadata", models.JSONField( blank=True, default=dict, verbose_name="Настройки без секретов" ), ), ( "created_at", models.DateTimeField(auto_now_add=True, verbose_name="Создано"), ), ( "updated_at", models.DateTimeField(auto_now=True, verbose_name="Обновлено"), ), ], options={ "verbose_name": "Источник данных", "verbose_name_plural": "Источники данных", "ordering": ["name"], }, ), migrations.CreateModel( name="Product", fields=[ ( "id", models.UUIDField( default=uuid.uuid4, editable=False, primary_key=True, serialize=False, ), ), ( "kind", models.CharField( choices=[ ("product", "Товар"), ("service", "Услуга"), ("bundle", "Комплект"), ], default="product", max_length=20, verbose_name="Тип", ), ), ( "sku", models.CharField( max_length=128, unique=True, verbose_name="Внутренний артикул" ), ), ("name", models.CharField(max_length=255, verbose_name="Название")), ( "barcode", models.CharField( blank=True, db_index=True, max_length=64, verbose_name="Штрихкод", ), ), ("description", models.TextField(blank=True, verbose_name="Описание")), ( "is_active", models.BooleanField(default=True, verbose_name="Активен"), ), ( "created_at", models.DateTimeField(auto_now_add=True, verbose_name="Создано"), ), ( "updated_at", models.DateTimeField(auto_now=True, verbose_name="Обновлено"), ), ( "parent", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="variants", to="commerce.product", verbose_name="Родительский товар", ), ), ], options={ "verbose_name": "Товар", "verbose_name_plural": "Товары", "ordering": ["name", "sku"], }, ), migrations.CreateModel( name="SourceRecord", fields=[ ( "id", models.UUIDField( default=uuid.uuid4, editable=False, primary_key=True, serialize=False, ), ), ( "entity_type", models.CharField(max_length=64, verbose_name="Тип объекта"), ), ( "external_id", models.CharField(max_length=255, verbose_name="Внешний ID"), ), ( "payload", models.JSONField(default=dict, verbose_name="Исходные данные"), ), ( "payload_hash", models.CharField( editable=False, max_length=64, verbose_name="Хеш данных" ), ), ( "source_updated_at", models.DateTimeField( blank=True, null=True, verbose_name="Изменено в источнике" ), ), ( "received_at", models.DateTimeField(auto_now=True, verbose_name="Получено"), ), ( "created_at", models.DateTimeField(auto_now_add=True, verbose_name="Создано"), ), ( "source", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="records", to="commerce.sourcesystem", verbose_name="Источник", ), ), ], options={ "verbose_name": "Исходная запись", "verbose_name_plural": "Исходные записи", }, ), migrations.CreateModel( name="ProductBinding", fields=[ ( "id", models.UUIDField( default=uuid.uuid4, editable=False, primary_key=True, serialize=False, ), ), ( "external_id", models.CharField(max_length=255, verbose_name="Внешний ID"), ), ( "external_sku", models.CharField( blank=True, db_index=True, max_length=128, verbose_name="Артикул в источнике", ), ), ( "is_primary", models.BooleanField( default=False, verbose_name="Основная привязка" ), ), ( "created_at", models.DateTimeField(auto_now_add=True, verbose_name="Создано"), ), ( "updated_at", models.DateTimeField(auto_now=True, verbose_name="Обновлено"), ), ( "product", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name="bindings", to="commerce.product", verbose_name="Товар", ), ), ( "source", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="product_bindings", to="commerce.sourcesystem", verbose_name="Источник", ), ), ], options={ "verbose_name": "Привязка товара", "verbose_name_plural": "Привязки товаров", }, ), migrations.CreateModel( name="Sale", fields=[ ( "id", models.UUIDField( default=uuid.uuid4, editable=False, primary_key=True, serialize=False, ), ), ( "external_id", models.CharField(max_length=255, verbose_name="Внешний ID"), ), ( "event_type", models.CharField( choices=[("sale", "Продажа"), ("return", "Возврат")], default="sale", max_length=16, verbose_name="Тип операции", ), ), ( "number", models.CharField(blank=True, max_length=128, verbose_name="Номер"), ), ( "channel", models.CharField( blank=True, db_index=True, max_length=64, verbose_name="Канал продаж", ), ), ( "status", models.CharField( blank=True, max_length=128, verbose_name="Статус источника" ), ), ( "counts_as_sale", models.BooleanField( default=True, help_text="Отключается для отменённых или тестовых заказов.", verbose_name="Учитывать в продажах", ), ), ( "currency", models.CharField( default="RUB", max_length=3, verbose_name="Валюта" ), ), ( "occurred_at", models.DateTimeField(db_index=True, verbose_name="Дата операции"), ), ( "source_updated_at", models.DateTimeField( blank=True, null=True, verbose_name="Изменено в источнике" ), ), ( "created_at", models.DateTimeField(auto_now_add=True, verbose_name="Создано"), ), ( "updated_at", models.DateTimeField(auto_now=True, verbose_name="Обновлено"), ), ( "source", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="sales", to="commerce.sourcesystem", verbose_name="Источник", ), ), ( "source_record", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="sales", to="commerce.sourcerecord", verbose_name="Исходная запись", ), ), ], options={ "verbose_name": "Продажа", "verbose_name_plural": "Продажи", "ordering": ["-occurred_at", "-created_at"], }, ), migrations.CreateModel( name="SaleLine", fields=[ ( "id", models.UUIDField( default=uuid.uuid4, editable=False, primary_key=True, serialize=False, ), ), ( "external_id", models.CharField(max_length=255, verbose_name="Внешний ID позиции"), ), ( "external_product_id", models.CharField( blank=True, max_length=255, verbose_name="Внешний ID товара" ), ), ( "external_sku", models.CharField( blank=True, max_length=128, verbose_name="Внешний артикул" ), ), ( "name", models.CharField( blank=True, max_length=255, verbose_name="Название в источнике" ), ), ( "quantity", models.DecimalField( decimal_places=4, default=Decimal("1"), max_digits=18, validators=[ django.core.validators.MinValueValidator(Decimal("0")) ], verbose_name="Количество", ), ), ( "gross_amount", models.DecimalField( decimal_places=2, default=Decimal("0"), max_digits=18, validators=[ django.core.validators.MinValueValidator(Decimal("0")) ], verbose_name="Сумма до скидки", ), ), ( "discount_amount", models.DecimalField( decimal_places=2, default=Decimal("0"), max_digits=18, validators=[ django.core.validators.MinValueValidator(Decimal("0")) ], verbose_name="Скидка", ), ), ( "cost_amount", models.DecimalField( decimal_places=2, default=Decimal("0"), max_digits=18, validators=[ django.core.validators.MinValueValidator(Decimal("0")) ], verbose_name="Себестоимость", ), ), ( "metadata", models.JSONField( blank=True, default=dict, verbose_name="Дополнительные данные" ), ), ( "product", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="sale_lines", to="commerce.product", verbose_name="Товар", ), ), ( "sale", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name="lines", to="commerce.sale", verbose_name="Продажа", ), ), ], options={ "verbose_name": "Позиция продажи", "verbose_name_plural": "Позиции продаж", }, ), migrations.CreateModel( name="Cost", fields=[ ( "id", models.UUIDField( default=uuid.uuid4, editable=False, primary_key=True, serialize=False, ), ), ( "external_id", models.CharField( blank=True, max_length=255, verbose_name="Внешний ID" ), ), ( "channel", models.CharField( blank=True, db_index=True, max_length=64, verbose_name="Канал продаж", ), ), ( "cost_type", models.CharField( choices=[ ("commission", "Комиссия"), ("delivery", "Доставка"), ("storage", "Хранение"), ("advertising", "Реклама"), ("acquiring", "Эквайринг"), ("return", "Возврат"), ("other", "Прочее"), ], max_length=32, verbose_name="Тип расхода", ), ), ( "amount", models.DecimalField( decimal_places=2, default=Decimal("0"), max_digits=18, verbose_name="Сумма", ), ), ( "currency", models.CharField( default="RUB", max_length=3, verbose_name="Валюта" ), ), ( "occurred_at", models.DateTimeField(db_index=True, verbose_name="Дата расхода"), ), ( "metadata", models.JSONField( blank=True, default=dict, verbose_name="Дополнительные данные" ), ), ( "created_at", models.DateTimeField(auto_now_add=True, verbose_name="Создано"), ), ( "product", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="costs", to="commerce.product", verbose_name="Товар", ), ), ( "sale_line", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name="costs", to="commerce.saleline", verbose_name="Позиция продажи", ), ), ( "source", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="costs", to="commerce.sourcesystem", verbose_name="Источник", ), ), ], options={"verbose_name": "Расход", "verbose_name_plural": "Расходы"}, ), migrations.CreateModel( name="StockSnapshot", fields=[ ( "id", models.UUIDField( default=uuid.uuid4, editable=False, primary_key=True, serialize=False, ), ), ( "warehouse_external_id", models.CharField(max_length=255, verbose_name="ID склада"), ), ( "warehouse_name", models.CharField(blank=True, max_length=255, verbose_name="Склад"), ), ( "quantity", models.DecimalField( decimal_places=4, default=Decimal("0"), max_digits=18, verbose_name="Остаток", ), ), ( "reserved", models.DecimalField( decimal_places=4, default=Decimal("0"), max_digits=18, verbose_name="Резерв", ), ), ( "in_transit", models.DecimalField( decimal_places=4, default=Decimal("0"), max_digits=18, verbose_name="В пути", ), ), ( "captured_at", models.DateTimeField(db_index=True, verbose_name="Дата снимка"), ), ( "metadata", models.JSONField( blank=True, default=dict, verbose_name="Дополнительные данные" ), ), ( "created_at", models.DateTimeField(auto_now_add=True, verbose_name="Создано"), ), ( "product", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name="stock_snapshots", to="commerce.product", verbose_name="Товар", ), ), ( "source", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="stock_snapshots", to="commerce.sourcesystem", verbose_name="Источник", ), ), ], options={ "verbose_name": "Снимок остатка", "verbose_name_plural": "Снимки остатков", "ordering": ["-captured_at"], }, ), migrations.AddIndex( model_name="product", index=models.Index( fields=["is_active", "name"], name="commerce_product_active_name" ), ), migrations.AddConstraint( model_name="product", constraint=models.CheckConstraint( condition=models.Q(("parent", models.F("id")), _negated=True), name="commerce_product_not_own_parent", ), ), migrations.AddConstraint( model_name="product", constraint=models.UniqueConstraint( django.db.models.functions.text.Lower("sku"), name="commerce_product_sku_case_insensitive", ), ), migrations.AddIndex( model_name="sourcerecord", index=models.Index( fields=["source", "entity_type", "source_updated_at"], name="commerce_source_record_sync", ), ), migrations.AddConstraint( model_name="sourcerecord", constraint=models.UniqueConstraint( fields=("source", "entity_type", "external_id"), name="commerce_source_record_identity", ), ), migrations.AddIndex( model_name="productbinding", index=models.Index( fields=["source", "external_sku"], name="commerce_binding_source_sku", ), ), migrations.AddConstraint( model_name="productbinding", constraint=models.UniqueConstraint( fields=("source", "external_id"), name="commerce_binding_source_external", ), ), migrations.AddIndex( model_name="sale", index=models.Index( fields=["counts_as_sale", "occurred_at"], name="commerce_sale_counted_date", ), ), migrations.AddConstraint( model_name="sale", constraint=models.UniqueConstraint( fields=("source", "external_id"), name="commerce_sale_source_external", ), ), migrations.AddIndex( model_name="saleline", index=models.Index( fields=["product", "sale"], name="commerce_line_product_sale" ), ), migrations.AddConstraint( model_name="saleline", constraint=models.UniqueConstraint( fields=("sale", "external_id"), name="commerce_sale_line_identity", ), ), migrations.AddConstraint( model_name="saleline", constraint=models.CheckConstraint( condition=models.Q(("discount_amount__lte", models.F("gross_amount"))), name="commerce_sale_line_discount_lte_gross", ), ), migrations.AddIndex( model_name="cost", index=models.Index( fields=["product", "occurred_at"], name="commerce_cost_product_date" ), ), migrations.AddConstraint( model_name="cost", constraint=models.CheckConstraint( condition=models.Q( ("product__isnull", False), ("sale_line__isnull", False), _connector="OR", ), name="commerce_cost_has_target", ), ), migrations.AddConstraint( model_name="cost", constraint=models.UniqueConstraint( condition=models.Q(("external_id", ""), _negated=True), fields=("source", "external_id", "cost_type"), name="commerce_cost_source_external_type", ), ), migrations.AddIndex( model_name="stocksnapshot", index=models.Index( fields=[ "product", "source", "warehouse_external_id", "-captured_at", ], name="commerce_stock_latest", ), ), migrations.AddConstraint( model_name="stocksnapshot", constraint=models.UniqueConstraint( fields=( "product", "source", "warehouse_external_id", "captured_at", ), name="commerce_stock_snapshot_identity", ), ), ]