134 lines
4.0 KiB
Python
134 lines
4.0 KiB
Python
"""add hierarchical expense memory governance fields
|
|
|
|
Revision ID: 20260716_0007
|
|
Revises: 20260716_0006
|
|
Create Date: 2026-07-16 12:05:00
|
|
"""
|
|
|
|
from collections.abc import Sequence
|
|
|
|
import sqlalchemy as sa
|
|
|
|
from alembic import op
|
|
|
|
revision: str = "20260716_0007"
|
|
down_revision: str | None = "20260716_0006"
|
|
branch_labels: str | Sequence[str] | None = None
|
|
depends_on: str | Sequence[str] | None = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column(
|
|
"memory_entries",
|
|
sa.Column(
|
|
"origin_type",
|
|
sa.String(length=24),
|
|
server_default="learned",
|
|
nullable=False,
|
|
),
|
|
)
|
|
op.add_column(
|
|
"memory_entries",
|
|
sa.Column("managed_by", sa.String(length=255), nullable=True),
|
|
)
|
|
op.add_column(
|
|
"memory_entries",
|
|
sa.Column("managed_at", sa.DateTime(timezone=True), nullable=True),
|
|
)
|
|
op.add_column(
|
|
"memory_entries",
|
|
sa.Column("management_reason", sa.String(length=255), nullable=True),
|
|
)
|
|
op.add_column(
|
|
"memory_entries",
|
|
sa.Column("policy_version", sa.String(length=64), nullable=True),
|
|
)
|
|
|
|
op.drop_constraint(
|
|
"ck_memory_entries_scope_type",
|
|
"memory_entries",
|
|
type_="check",
|
|
)
|
|
op.create_check_constraint(
|
|
"ck_memory_entries_scope_type",
|
|
"memory_entries",
|
|
"scope_type IN ('user', 'department', 'enterprise')",
|
|
)
|
|
op.create_check_constraint(
|
|
"ck_memory_entries_origin_type",
|
|
"memory_entries",
|
|
"origin_type IN ('learned', 'admin_managed')",
|
|
)
|
|
op.create_check_constraint(
|
|
"ck_memory_entries_scope_origin",
|
|
"memory_entries",
|
|
"(scope_type = 'user' AND origin_type = 'learned') OR "
|
|
"(scope_type IN ('department', 'enterprise') "
|
|
"AND origin_type = 'admin_managed')",
|
|
)
|
|
op.create_check_constraint(
|
|
"ck_memory_entries_enterprise_scope",
|
|
"memory_entries",
|
|
"scope_type != 'enterprise' OR scope_id = tenant_id",
|
|
)
|
|
op.create_check_constraint(
|
|
"ck_memory_entries_management_audit",
|
|
"memory_entries",
|
|
"(origin_type = 'learned' AND managed_by IS NULL "
|
|
"AND managed_at IS NULL AND management_reason IS NULL) OR "
|
|
"(origin_type = 'admin_managed' AND managed_by IS NOT NULL "
|
|
"AND length(trim(managed_by)) > 0 AND managed_at IS NOT NULL "
|
|
"AND management_reason IS NOT NULL "
|
|
"AND length(trim(management_reason)) > 0 "
|
|
"AND policy_version IS NOT NULL AND length(trim(policy_version)) > 0)",
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_constraint(
|
|
"ck_memory_entries_management_audit",
|
|
"memory_entries",
|
|
type_="check",
|
|
)
|
|
op.drop_constraint(
|
|
"ck_memory_entries_enterprise_scope",
|
|
"memory_entries",
|
|
type_="check",
|
|
)
|
|
op.drop_constraint(
|
|
"ck_memory_entries_scope_origin",
|
|
"memory_entries",
|
|
type_="check",
|
|
)
|
|
op.drop_constraint(
|
|
"ck_memory_entries_origin_type",
|
|
"memory_entries",
|
|
type_="check",
|
|
)
|
|
op.drop_constraint(
|
|
"ck_memory_entries_scope_type",
|
|
"memory_entries",
|
|
type_="check",
|
|
)
|
|
# 0006 只认识个人记忆。组织记忆无法无损映射回旧结构,因此降级时
|
|
# 先解除组织版本间的自引用并删除组织作用域记录,个人记忆完整保留。
|
|
op.execute(
|
|
"UPDATE memory_entries SET superseded_by_id = NULL "
|
|
"WHERE scope_type IN ('department', 'enterprise')"
|
|
)
|
|
op.execute(
|
|
"DELETE FROM memory_entries "
|
|
"WHERE scope_type IN ('department', 'enterprise')"
|
|
)
|
|
op.create_check_constraint(
|
|
"ck_memory_entries_scope_type",
|
|
"memory_entries",
|
|
"scope_type = 'user'",
|
|
)
|
|
|
|
op.drop_column("memory_entries", "policy_version")
|
|
op.drop_column("memory_entries", "management_reason")
|
|
op.drop_column("memory_entries", "managed_at")
|
|
op.drop_column("memory_entries", "managed_by")
|
|
op.drop_column("memory_entries", "origin_type")
|