"""Add Intasect project and personnel lifecycle foundation. Revision ID: 202607120001 Revises: 202607090001 Create Date: 2026-07-12 """ from alembic import op import sqlalchemy as sa revision = "202607120001" down_revision = "202607090001" branch_labels = None depends_on = None def _indexes(table_name: str, columns: list[tuple[str, bool]]) -> None: for column_name, unique in columns: op.create_index( op.f(f"ix_{table_name}_{column_name}"), table_name, [column_name], unique=unique, ) def _add_columns(table_name: str, columns: list[sa.Column]) -> None: for column in columns: op.add_column(table_name, column) def upgrade() -> None: op.create_table( "employees", sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), sa.Column("code", sa.String(length=64), nullable=False), sa.Column("name", sa.String(length=128), nullable=False), sa.Column("department_code", sa.String(length=64), nullable=True), sa.Column("department_name", sa.String(length=128), nullable=True), sa.Column("title", sa.String(length=128), nullable=True), sa.Column("employment_status", sa.String(length=32), nullable=False), sa.Column("hired_at", sa.DateTime(), nullable=True), sa.Column("ding_user_id", sa.String(length=64), nullable=True), sa.Column("source_system", sa.String(length=64), nullable=False), sa.Column("external_id", sa.String(length=128), nullable=False), sa.Column("source_created_at", sa.DateTime(), nullable=True), sa.Column("source_updated_at", sa.DateTime(), nullable=True), sa.Column("last_seen_at", sa.DateTime(), nullable=True), sa.Column("is_active", sa.Boolean(), nullable=False), sa.Column("created_at", sa.DateTime(), nullable=False), sa.Column("updated_at", sa.DateTime(), nullable=False), sa.PrimaryKeyConstraint("id"), ) _indexes( "employees", [ ("code", True), ("name", False), ("department_code", False), ("department_name", False), ("employment_status", False), ("ding_user_id", False), ("external_id", False), ("source_updated_at", False), ("last_seen_at", False), ("is_active", False), ], ) op.create_table( "project_members", sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), sa.Column("code", sa.String(length=64), nullable=False), sa.Column("project_code", sa.String(length=64), nullable=False), sa.Column("employee_code", sa.String(length=64), nullable=False), sa.Column("role_name", sa.String(length=128), nullable=True), sa.Column("is_leader", sa.Boolean(), nullable=False), sa.Column("is_resident", sa.Boolean(), nullable=False), sa.Column("work_mode", sa.Text(), nullable=True), sa.Column("planned_days", sa.Integer(), nullable=True), sa.Column("workload_percent", sa.Integer(), nullable=True), sa.Column("source_system", sa.String(length=64), nullable=False), sa.Column("external_id", sa.String(length=128), nullable=False), sa.Column("source_created_at", sa.DateTime(), nullable=True), sa.Column("last_seen_at", sa.DateTime(), nullable=True), sa.Column("is_active", sa.Boolean(), nullable=False), sa.Column("created_at", sa.DateTime(), nullable=False), sa.Column("updated_at", sa.DateTime(), nullable=False), sa.PrimaryKeyConstraint("id"), ) _indexes( "project_members", [ ("code", True), ("project_code", False), ("employee_code", False), ("role_name", False), ("is_leader", False), ("external_id", False), ("last_seen_at", False), ("is_active", False), ], ) op.create_table( "project_milestones", sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), sa.Column("code", sa.String(length=128), nullable=False), sa.Column("project_code", sa.String(length=64), nullable=False), sa.Column("source_stage_id", sa.String(length=64), nullable=False), sa.Column("stage_name", sa.String(length=128), nullable=False), sa.Column("plan_start", sa.Date(), nullable=True), sa.Column("plan_end", sa.Date(), nullable=True), sa.Column("status", sa.String(length=32), nullable=False), sa.Column("is_overdue", sa.Boolean(), nullable=False), sa.Column("activity_total", sa.Integer(), nullable=False), sa.Column("activity_completed", sa.Integer(), nullable=False), sa.Column("source_system", sa.String(length=64), nullable=False), sa.Column("external_id", sa.String(length=128), nullable=False), sa.Column("source_updated_at", sa.DateTime(), nullable=True), sa.Column("last_seen_at", sa.DateTime(), nullable=True), sa.Column("is_active", sa.Boolean(), nullable=False), sa.Column("created_at", sa.DateTime(), nullable=False), sa.Column("updated_at", sa.DateTime(), nullable=False), sa.PrimaryKeyConstraint("id"), ) _indexes( "project_milestones", [ ("code", True), ("project_code", False), ("source_stage_id", False), ("stage_name", False), ("plan_end", False), ("status", False), ("is_overdue", False), ("external_id", False), ("source_updated_at", False), ("last_seen_at", False), ("is_active", False), ], ) op.create_table( "source_sync_cursors", sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), sa.Column("dataset", sa.String(length=64), nullable=False), sa.Column("status", sa.String(length=32), nullable=False), sa.Column("watermark_at", sa.DateTime(), nullable=True), sa.Column("watermark_key", sa.String(length=128), nullable=True), sa.Column("last_run_code", sa.String(length=64), nullable=True), sa.Column("last_success_at", sa.DateTime(), nullable=True), sa.Column("processed_count", sa.Integer(), nullable=False), sa.Column("error_message", sa.Text(), nullable=True), sa.Column("created_at", sa.DateTime(), nullable=False), sa.Column("updated_at", sa.DateTime(), nullable=False), sa.PrimaryKeyConstraint("id"), ) _indexes( "source_sync_cursors", [ ("dataset", True), ("status", False), ("last_run_code", False), ("last_success_at", False), ], ) _add_columns( "projects", [ sa.Column("display_code", sa.String(length=64), nullable=True), sa.Column("department_code", sa.String(length=64), nullable=True), sa.Column("department_name", sa.String(length=128), nullable=True), sa.Column("owner_employee_code", sa.String(length=64), nullable=True), sa.Column("source_stage", sa.String(length=32), nullable=True), sa.Column("source_stage_label", sa.String(length=128), nullable=True), sa.Column("source_archived", sa.Boolean(), nullable=False, server_default=sa.false()), sa.Column("source_created_at", sa.DateTime(), nullable=True), sa.Column("source_updated_at", sa.DateTime(), nullable=True), sa.Column("last_seen_at", sa.DateTime(), nullable=True), sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()), ], ) _indexes( "projects", [ ("display_code", False), ("department_code", False), ("department_name", False), ("owner_employee_code", False), ("source_stage", False), ("source_archived", False), ("source_updated_at", False), ("last_seen_at", False), ("is_active", False), ], ) _add_columns( "work_tasks", [ sa.Column("employee_code", sa.String(length=64), nullable=True), sa.Column("source_created_at", sa.DateTime(), nullable=True), sa.Column("source_updated_at", sa.DateTime(), nullable=True), sa.Column("last_seen_at", sa.DateTime(), nullable=True), sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()), ], ) _indexes( "work_tasks", [ ("employee_code", False), ("source_updated_at", False), ("last_seen_at", False), ("is_active", False), ], ) _add_columns( "attendance_records", [ sa.Column( "attendance_scope", sa.String(length=32), nullable=False, server_default="company" ), sa.Column("source_status", sa.String(length=64), nullable=True), sa.Column("source_location_status", sa.String(length=64), nullable=True), sa.Column("source_updated_at", sa.DateTime(), nullable=True), sa.Column("last_seen_at", sa.DateTime(), nullable=True), sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()), ], ) _indexes( "attendance_records", [ ("attendance_scope", False), ("source_status", False), ("source_updated_at", False), ("last_seen_at", False), ("is_active", False), ], ) _add_columns( "work_reports", [ sa.Column("employee_code", sa.String(length=64), nullable=True), sa.Column("external_id", sa.String(length=128), nullable=True), sa.Column("is_late", sa.Boolean(), nullable=False, server_default=sa.false()), sa.Column("is_draft", sa.Boolean(), nullable=False, server_default=sa.false()), sa.Column("source_updated_at", sa.DateTime(), nullable=True), sa.Column("last_seen_at", sa.DateTime(), nullable=True), sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()), ], ) _indexes( "work_reports", [ ("employee_code", False), ("external_id", False), ("is_late", False), ("is_draft", False), ("source_updated_at", False), ("last_seen_at", False), ("is_active", False), ], ) op.add_column( "report_push_runs", sa.Column("idempotency_key", sa.String(length=128), nullable=True) ) op.create_index( op.f("ix_report_push_runs_idempotency_key"), "report_push_runs", ["idempotency_key"], unique=True, ) def downgrade() -> None: op.drop_index(op.f("ix_report_push_runs_idempotency_key"), table_name="report_push_runs") op.drop_column("report_push_runs", "idempotency_key") index_columns = { "work_reports": { "employee_code", "external_id", "is_late", "is_draft", "source_updated_at", "last_seen_at", "is_active", }, "attendance_records": { "attendance_scope", "source_status", "source_updated_at", "last_seen_at", "is_active", }, "work_tasks": {"employee_code", "source_updated_at", "last_seen_at", "is_active"}, "projects": { "display_code", "department_code", "department_name", "owner_employee_code", "source_stage", "source_archived", "source_updated_at", "last_seen_at", "is_active", }, } for table_name, columns in { "work_reports": [ "employee_code", "external_id", "is_late", "is_draft", "source_updated_at", "last_seen_at", "is_active", ], "attendance_records": [ "attendance_scope", "source_status", "source_location_status", "source_updated_at", "last_seen_at", "is_active", ], "work_tasks": [ "employee_code", "source_created_at", "source_updated_at", "last_seen_at", "is_active", ], "projects": [ "display_code", "department_code", "department_name", "owner_employee_code", "source_stage", "source_stage_label", "source_archived", "source_created_at", "source_updated_at", "last_seen_at", "is_active", ], }.items(): for column_name in reversed(columns): if column_name in index_columns[table_name]: op.drop_index(op.f(f"ix_{table_name}_{column_name}"), table_name=table_name) op.drop_column(table_name, column_name) for table_name in ["source_sync_cursors", "project_milestones", "project_members", "employees"]: op.drop_table(table_name)