"""Phase 2: Listen, Mitgliedschaften, Märkte, Warengruppen, Artikel, Einträge Revision ID: 0002 Revises: 0001 Create Date: 2026-08-07 """ from collections.abc import Sequence import sqlalchemy as sa from alembic import op revision: str = "0002" down_revision: str | None = "0001" branch_labels: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None MYSQL = { "mysql_engine": "InnoDB", "mysql_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci", } def upgrade() -> None: op.create_table( "shopping_list", sa.Column("id", sa.String(36), primary_key=True), sa.Column("name", sa.String(120), nullable=False), sa.Column("owner_id", sa.String(36), nullable=False), sa.Column("rev", sa.BigInteger(), nullable=False, server_default=sa.text("0")), sa.Column("created_at", sa.DateTime(), nullable=False, server_default=sa.func.now()), sa.Column("updated_at", sa.DateTime(), nullable=False, server_default=sa.func.now()), sa.Column("deleted_at", sa.DateTime(), nullable=True), sa.ForeignKeyConstraint(["owner_id"], ["user.id"], ondelete="RESTRICT"), **MYSQL, ) op.create_table( "list_member", sa.Column("list_id", sa.String(36), primary_key=True), sa.Column("user_id", sa.String(36), primary_key=True), sa.Column("role", sa.String(16), nullable=False, server_default="editor"), sa.Column("joined_at", sa.DateTime(), nullable=False, server_default=sa.func.now()), sa.ForeignKeyConstraint(["list_id"], ["shopping_list.id"], ondelete="CASCADE"), sa.ForeignKeyConstraint(["user_id"], ["user.id"], ondelete="CASCADE"), **MYSQL, ) op.create_index("ix_list_member_user", "list_member", ["user_id"]) op.create_table( "market", sa.Column("id", sa.String(36), primary_key=True), sa.Column("list_id", sa.String(36), nullable=False), sa.Column("name", sa.String(120), nullable=False), sa.Column("sort_order", sa.Integer(), nullable=False, server_default=sa.text("0")), sa.Column("row_rev", sa.BigInteger(), nullable=False, server_default=sa.text("0")), sa.Column("deleted_at", sa.DateTime(), nullable=True), sa.ForeignKeyConstraint(["list_id"], ["shopping_list.id"], ondelete="CASCADE"), sa.UniqueConstraint("list_id", "name", name="uq_market_list_name"), **MYSQL, ) op.create_index("ix_market_list_rev", "market", ["list_id", "row_rev"]) op.create_table( "category", sa.Column("id", sa.String(36), primary_key=True), sa.Column("list_id", sa.String(36), nullable=False), sa.Column("name", sa.String(120), nullable=False), sa.Column("sort_order", sa.Integer(), nullable=False, server_default=sa.text("0")), sa.Column("row_rev", sa.BigInteger(), nullable=False, server_default=sa.text("0")), sa.Column("deleted_at", sa.DateTime(), nullable=True), sa.ForeignKeyConstraint(["list_id"], ["shopping_list.id"], ondelete="CASCADE"), sa.UniqueConstraint("list_id", "name", name="uq_category_list_name"), **MYSQL, ) op.create_index("ix_category_list_rev", "category", ["list_id", "row_rev"]) op.create_table( "article", sa.Column("id", sa.String(36), primary_key=True), sa.Column("list_id", sa.String(36), nullable=False), sa.Column("name", sa.String(200), nullable=False), sa.Column("barcode", sa.String(64), nullable=True), sa.Column("note", sa.String(500), nullable=True), sa.Column("default_market_id", sa.String(36), nullable=True), sa.Column("default_category_id", sa.String(36), nullable=True), sa.Column("row_rev", sa.BigInteger(), nullable=False, server_default=sa.text("0")), sa.Column("deleted_at", sa.DateTime(), nullable=True), sa.ForeignKeyConstraint(["list_id"], ["shopping_list.id"], ondelete="CASCADE"), sa.ForeignKeyConstraint(["default_market_id"], ["market.id"], ondelete="SET NULL"), sa.ForeignKeyConstraint(["default_category_id"], ["category.id"], ondelete="SET NULL"), sa.UniqueConstraint("list_id", "name", name="uq_article_list_name"), **MYSQL, ) op.create_index("ix_article_list_rev", "article", ["list_id", "row_rev"]) op.create_index("ix_article_barcode", "article", ["list_id", "barcode"]) op.create_table( "article_attribute", sa.Column("id", sa.String(36), primary_key=True), sa.Column("article_id", sa.String(36), nullable=False), sa.Column("attr_name", sa.String(80), nullable=False), sa.Column("attr_value", sa.String(300), nullable=False), sa.ForeignKeyConstraint(["article_id"], ["article.id"], ondelete="CASCADE"), sa.UniqueConstraint("article_id", "attr_name", name="uq_article_attr"), **MYSQL, ) op.create_table( "article_market", sa.Column("article_id", sa.String(36), primary_key=True), sa.Column("market_id", sa.String(36), primary_key=True), sa.ForeignKeyConstraint(["article_id"], ["article.id"], ondelete="CASCADE"), sa.ForeignKeyConstraint(["market_id"], ["market.id"], ondelete="CASCADE"), **MYSQL, ) op.create_table( "list_item", sa.Column("id", sa.String(36), primary_key=True), sa.Column("list_id", sa.String(36), nullable=False), sa.Column("article_id", sa.String(36), nullable=False), sa.Column("market_id", sa.String(36), nullable=True), sa.Column("category_id", sa.String(36), nullable=True), sa.Column("quantity", sa.Numeric(10, 3), nullable=True), sa.Column("unit", sa.String(32), nullable=True), sa.Column("note", sa.String(500), nullable=True), sa.Column("status", sa.String(16), nullable=False, server_default="open"), sa.Column("price_cents", sa.Integer(), nullable=True), sa.Column("row_rev", sa.BigInteger(), nullable=False, server_default=sa.text("0")), sa.Column("created_at", sa.DateTime(), nullable=False, server_default=sa.func.now()), sa.Column("updated_at", sa.DateTime(), nullable=False, server_default=sa.func.now()), sa.Column("deleted_at", sa.DateTime(), nullable=True), sa.ForeignKeyConstraint(["list_id"], ["shopping_list.id"], ondelete="CASCADE"), sa.ForeignKeyConstraint(["article_id"], ["article.id"], ondelete="CASCADE"), sa.ForeignKeyConstraint(["market_id"], ["market.id"], ondelete="SET NULL"), sa.ForeignKeyConstraint(["category_id"], ["category.id"], ondelete="SET NULL"), **MYSQL, ) op.create_index("ix_list_item_list_rev", "list_item", ["list_id", "row_rev"]) op.create_index("ix_list_item_list_status", "list_item", ["list_id", "status"]) def downgrade() -> None: op.drop_table("list_item") op.drop_table("article_market") op.drop_table("article_attribute") op.drop_table("article") op.drop_table("category") op.drop_table("market") op.drop_index("ix_list_member_user", table_name="list_member") op.drop_table("list_member") op.drop_table("shopping_list")