"""initial schema Revision ID: 001 Revises: Create Date: 2026-04-06 """ from typing import Sequence, Union import sqlalchemy as sa from alembic import op import sqlalchemy.dialects.postgresql as pg revision: str = "001" down_revision: Union[str, None] = None branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: # Enums op.execute("CREATE TYPE tenant_role AS ENUM ('READ_ONLY', 'READ_WRITE', 'TENANT_ADMIN')") op.execute("CREATE TYPE nutrient_demand AS ENUM ('schwach', 'mittel', 'stark')") op.execute("CREATE TYPE water_demand AS ENUM ('wenig', 'mittel', 'viel')") op.execute("CREATE TYPE compatibility_rating AS ENUM ('gut', 'neutral', 'schlecht')") op.execute("CREATE TYPE location_type AS ENUM ('sonnig', 'halbschatten', 'schatten')") op.execute("CREATE TYPE soil_type AS ENUM ('normal', 'sandig', 'lehmig', 'humusreich')") # users op.create_table( "users", sa.Column("id", pg.UUID(as_uuid=True), primary_key=True), sa.Column("email", sa.String(255), nullable=False, unique=True), sa.Column("hashed_password", sa.String(255), nullable=False), sa.Column("full_name", sa.String(255), nullable=False), sa.Column("is_active", sa.Boolean, nullable=False, server_default="true"), sa.Column("is_superadmin", sa.Boolean, nullable=False, server_default="false"), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), ) op.create_index("ix_users_id", "users", ["id"]) op.create_index("ix_users_email", "users", ["email"]) # tenants op.create_table( "tenants", sa.Column("id", pg.UUID(as_uuid=True), primary_key=True), sa.Column("name", sa.String(255), nullable=False), sa.Column("slug", sa.String(100), nullable=False, unique=True), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), ) op.create_index("ix_tenants_id", "tenants", ["id"]) op.create_index("ix_tenants_slug", "tenants", ["slug"]) # user_tenants op.create_table( "user_tenants", sa.Column("user_id", pg.UUID(as_uuid=True), sa.ForeignKey("users.id", ondelete="CASCADE"), primary_key=True), sa.Column("tenant_id", pg.UUID(as_uuid=True), sa.ForeignKey("tenants.id", ondelete="CASCADE"), primary_key=True), sa.Column("role", sa.Enum("READ_ONLY", "READ_WRITE", "TENANT_ADMIN", name="tenant_role", create_type=False), nullable=False), sa.UniqueConstraint("user_id", "tenant_id", name="uq_user_tenant"), ) # plant_families op.create_table( "plant_families", sa.Column("id", pg.UUID(as_uuid=True), primary_key=True), sa.Column("name", sa.String(255), nullable=False, unique=True), sa.Column("latin_name", sa.String(255), nullable=False, unique=True), ) op.create_index("ix_plant_families_id", "plant_families", ["id"]) # plants op.create_table( "plants", sa.Column("id", pg.UUID(as_uuid=True), primary_key=True), sa.Column("tenant_id", pg.UUID(as_uuid=True), sa.ForeignKey("tenants.id", ondelete="CASCADE"), nullable=True), sa.Column("family_id", pg.UUID(as_uuid=True), sa.ForeignKey("plant_families.id", ondelete="RESTRICT"), nullable=False), sa.Column("name", sa.String(255), nullable=False), sa.Column("latin_name", sa.String(255), nullable=True), sa.Column("nutrient_demand", sa.Enum("schwach", "mittel", "stark", name="nutrient_demand", create_type=False), nullable=False), sa.Column("water_demand", sa.Enum("wenig", "mittel", "viel", name="water_demand", create_type=False), nullable=False), sa.Column("spacing_cm", sa.Integer, nullable=False), sa.Column("sowing_start_month", sa.Integer, nullable=False), sa.Column("sowing_end_month", sa.Integer, nullable=False), sa.Column("rest_years", sa.Integer, nullable=False, server_default="0"), sa.Column("notes", sa.Text, nullable=True), sa.Column("is_active", sa.Boolean, nullable=False, server_default="true"), ) op.create_index("ix_plants_id", "plants", ["id"]) op.create_index("ix_plants_tenant_id", "plants", ["tenant_id"]) op.create_index("ix_plants_family_id", "plants", ["family_id"]) # plant_compatibilities op.create_table( "plant_compatibilities", sa.Column("id", pg.UUID(as_uuid=True), primary_key=True), sa.Column("plant_id_a", pg.UUID(as_uuid=True), sa.ForeignKey("plants.id", ondelete="CASCADE"), nullable=False), sa.Column("plant_id_b", pg.UUID(as_uuid=True), sa.ForeignKey("plants.id", ondelete="CASCADE"), nullable=False), sa.Column("rating", sa.Enum("gut", "neutral", "schlecht", name="compatibility_rating", create_type=False), nullable=False), sa.Column("reason", sa.Text, nullable=True), sa.UniqueConstraint("plant_id_a", "plant_id_b", name="uq_plant_compatibility"), ) # beds op.create_table( "beds", sa.Column("id", pg.UUID(as_uuid=True), primary_key=True), sa.Column("tenant_id", pg.UUID(as_uuid=True), sa.ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False), sa.Column("name", sa.String(255), nullable=False), sa.Column("width_m", sa.Numeric(5, 2), nullable=False), sa.Column("length_m", sa.Numeric(5, 2), nullable=False), sa.Column("location", sa.Enum("sonnig", "halbschatten", "schatten", name="location_type", create_type=False), nullable=False), sa.Column("soil_type", sa.Enum("normal", "sandig", "lehmig", "humusreich", name="soil_type", create_type=False), nullable=False, server_default="normal"), sa.Column("notes", sa.Text, nullable=True), sa.Column("is_active", sa.Boolean, nullable=False, server_default="true"), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), ) op.create_index("ix_beds_id", "beds", ["id"]) op.create_index("ix_beds_tenant_id", "beds", ["tenant_id"]) # bed_plantings op.create_table( "bed_plantings", sa.Column("id", pg.UUID(as_uuid=True), primary_key=True), sa.Column("bed_id", pg.UUID(as_uuid=True), sa.ForeignKey("beds.id", ondelete="CASCADE"), nullable=False), sa.Column("plant_id", pg.UUID(as_uuid=True), sa.ForeignKey("plants.id", ondelete="RESTRICT"), nullable=False), sa.Column("area_m2", sa.Numeric(5, 2), nullable=True), sa.Column("count", sa.Integer, nullable=True), sa.Column("planted_date", sa.Date, nullable=True), sa.Column("removed_date", sa.Date, nullable=True), sa.Column("notes", sa.Text, nullable=True), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), ) op.create_index("ix_bed_plantings_id", "bed_plantings", ["id"]) op.create_index("ix_bed_plantings_bed_id", "bed_plantings", ["bed_id"]) op.create_index("ix_bed_plantings_plant_id", "bed_plantings", ["plant_id"]) def downgrade() -> None: op.drop_table("bed_plantings") op.drop_table("beds") op.drop_table("plant_compatibilities") op.drop_table("plants") op.drop_table("plant_families") op.drop_table("user_tenants") op.drop_table("tenants") op.drop_table("users") op.execute("DROP TYPE IF EXISTS soil_type") op.execute("DROP TYPE IF EXISTS location_type") op.execute("DROP TYPE IF EXISTS compatibility_rating") op.execute("DROP TYPE IF EXISTS water_demand") op.execute("DROP TYPE IF EXISTS nutrient_demand") op.execute("DROP TYPE IF EXISTS tenant_role")