"""Checkpoint C1: socle compteurs, rattachements et relevés audités.""" from alembic import op import sqlalchemy as sa revision = "n9c0d1e2f3g4" down_revision = "m8b9c0d1e2f3" branch_labels = None depends_on = None def upgrade(): # Les relevés restent auditables après suppression d'un utilisateur ; # seule la référence vers l'auteur devient NULL. op.drop_constraint("meter_readings_ibfk_2", "meter_readings", type_="foreignkey") op.create_foreign_key( "fk_meter_readings_read_by", "meter_readings", "users", ["read_by_id"], ["id"], ondelete="SET NULL", ) op.alter_column("meters", "equipment_id", existing_type=sa.Integer(), nullable=True) for name, target in ( ("building_id", "buildings"), ("zone_id", "zones"), ("room_id", "rooms"), ("housing_unit_id", "housing_units"), ): op.add_column("meters", sa.Column(name, sa.Integer(), nullable=True)) op.create_index(f"ix_meters_{name}", "meters", [name]) op.create_foreign_key(f"fk_meters_{name}", "meters", target, [name], ["id"]) op.add_column("meters", sa.Column("parent_id", sa.Integer(), nullable=True)) op.add_column("meters", sa.Column("replaces_meter_id", sa.Integer(), nullable=True)) op.add_column("meters", sa.Column("usage", sa.String(255), nullable=True)) op.add_column("meters", sa.Column("status", sa.String(30), nullable=False, server_default="active")) op.add_column("meters", sa.Column("remainder_label", sa.String(255), nullable=True)) for name in ("parent_id", "replaces_meter_id"): op.create_index(f"ix_meters_{name}", "meters", [name]) op.create_foreign_key(f"fk_meters_{name}", "meters", "meters", [name], ["id"]) op.create_check_constraint( "ck_meters_one_scope", "meters", "(equipment_id IS NOT NULL) + (building_id IS NOT NULL) + " "(zone_id IS NOT NULL) + (room_id IS NOT NULL) + " "(housing_unit_id IS NOT NULL) = 1", ) op.add_column("meter_readings", sa.Column("photo_filename", sa.String(255), nullable=True)) op.add_column("meter_readings", sa.Column("photo_path", sa.String(500), nullable=True)) op.create_table( "meter_reading_corrections", sa.Column("id", sa.Integer(), primary_key=True), sa.Column("reading_id", sa.Integer(), nullable=False), sa.Column("old_value", sa.Float(), nullable=False), sa.Column("new_value", sa.Float(), nullable=False), sa.Column("corrected_by_id", sa.Integer(), nullable=True), sa.Column("corrected_at", sa.DateTime(), nullable=False), sa.Column("reason", sa.Text(), nullable=False), sa.ForeignKeyConstraint(["reading_id"], ["meter_readings.id"], ondelete="CASCADE"), sa.ForeignKeyConstraint(["corrected_by_id"], ["users.id"], ondelete="SET NULL"), ) op.create_index("ix_meter_reading_corrections_reading_id", "meter_reading_corrections", ["reading_id"]) op.create_index("ix_meter_reading_corrections_corrected_by_id", "meter_reading_corrections", ["corrected_by_id"]) def downgrade(): op.drop_constraint("fk_meter_readings_read_by", "meter_readings", type_="foreignkey") op.create_foreign_key("meter_readings_ibfk_2", "meter_readings", "users", ["read_by_id"], ["id"]) op.drop_index("ix_meter_reading_corrections_corrected_by_id", table_name="meter_reading_corrections") op.drop_index("ix_meter_reading_corrections_reading_id", table_name="meter_reading_corrections") op.drop_table("meter_reading_corrections") op.drop_column("meter_readings", "photo_path") op.drop_column("meter_readings", "photo_filename") op.drop_constraint("ck_meters_one_scope", "meters", type_="check") for name in ("replaces_meter_id", "parent_id"): op.drop_constraint(f"fk_meters_{name}", "meters", type_="foreignkey") op.drop_index(f"ix_meters_{name}", table_name="meters") op.drop_column("meters", name) for name in ("remainder_label", "status", "usage"): op.drop_column("meters", name) for name, target in reversed((("building_id", "buildings"), ("zone_id", "zones"), ("room_id", "rooms"), ("housing_unit_id", "housing_units"))): op.drop_constraint(f"fk_meters_{name}", "meters", type_="foreignkey") op.drop_index(f"ix_meters_{name}", table_name="meters") op.drop_column("meters", name) op.alter_column("meters", "equipment_id", existing_type=sa.Integer(), nullable=False)