first commit
This commit is contained in:
@@ -0,0 +1,374 @@
|
||||
"""initial support schema
|
||||
|
||||
Revision ID: 0001_initial_support_schema
|
||||
Revises:
|
||||
Create Date: 2026-07-08 00:00:00
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision = "0001_initial_support_schema"
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
support_status_codes = sa.table(
|
||||
"support_status_codes",
|
||||
sa.column("code", sa.String(length=20)),
|
||||
sa.column("name", sa.String(length=50)),
|
||||
sa.column("sort_order", sa.Integer()),
|
||||
)
|
||||
|
||||
support_category_codes = sa.table(
|
||||
"support_category_codes",
|
||||
sa.column("code", sa.String(length=20)),
|
||||
sa.column("name", sa.String(length=50)),
|
||||
sa.column("sort_order", sa.Integer()),
|
||||
)
|
||||
|
||||
service_types = sa.table(
|
||||
"service_types",
|
||||
sa.column("id", sa.BigInteger()),
|
||||
sa.column("service_code", sa.String(length=50)),
|
||||
sa.column("service_name", sa.String(length=100)),
|
||||
sa.column("description", sa.Text()),
|
||||
sa.column("is_active", sa.Boolean()),
|
||||
)
|
||||
|
||||
workspaces = sa.table(
|
||||
"workspaces",
|
||||
sa.column("workspace_type", sa.String(length=20)),
|
||||
sa.column("software_app_id", sa.BigInteger()),
|
||||
sa.column("service_type_id", sa.BigInteger()),
|
||||
sa.column("workspace_code", sa.String(length=50)),
|
||||
sa.column("workspace_name", sa.String(length=100)),
|
||||
sa.column("is_active", sa.Boolean()),
|
||||
)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"software_apps",
|
||||
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
||||
sa.Column("app_code", sa.String(length=50), nullable=False),
|
||||
sa.Column("app_name", sa.String(length=100), nullable=False),
|
||||
sa.Column("description", sa.Text(), nullable=True),
|
||||
sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("created_at", sa.TIMESTAMP(), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("app_code", name="uq_software_apps_app_code"),
|
||||
sa.UniqueConstraint("app_name", name="uq_software_apps_app_name"),
|
||||
mysql_engine="InnoDB",
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"service_types",
|
||||
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
||||
sa.Column("service_code", sa.String(length=50), nullable=False),
|
||||
sa.Column("service_name", sa.String(length=100), nullable=False),
|
||||
sa.Column("description", sa.Text(), nullable=True),
|
||||
sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("created_at", sa.TIMESTAMP(), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("service_code", name="uq_service_types_service_code"),
|
||||
sa.UniqueConstraint("service_name", name="uq_service_types_service_name"),
|
||||
mysql_engine="InnoDB",
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"workspaces",
|
||||
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
||||
sa.Column("workspace_type", sa.String(length=20), nullable=False),
|
||||
sa.Column("software_app_id", sa.BigInteger(), nullable=True),
|
||||
sa.Column("service_type_id", sa.BigInteger(), nullable=True),
|
||||
sa.Column("workspace_code", sa.String(length=50), nullable=False),
|
||||
sa.Column("workspace_name", sa.String(length=100), nullable=False),
|
||||
sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("created_at", sa.TIMESTAMP(), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")),
|
||||
sa.CheckConstraint(
|
||||
"(workspace_type = 'SOFTWARE_APP' AND software_app_id IS NOT NULL AND service_type_id IS NULL) OR "
|
||||
"(workspace_type = 'INTRANET_SERVICE' AND service_type_id IS NOT NULL AND software_app_id IS NULL)",
|
||||
name="chk_workspaces_scope",
|
||||
),
|
||||
sa.ForeignKeyConstraint(["service_type_id"], ["service_types.id"], name="fk_workspaces_service_type"),
|
||||
sa.ForeignKeyConstraint(["software_app_id"], ["software_apps.id"], name="fk_workspaces_software_app"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("workspace_code", name="uq_workspaces_workspace_code"),
|
||||
mysql_engine="InnoDB",
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"user_workspace_access",
|
||||
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
||||
sa.Column("user_id", sa.String(length=100), nullable=False),
|
||||
sa.Column("tenant_id", sa.String(length=100), nullable=False),
|
||||
sa.Column("workspace_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("workspace_role", sa.String(length=30), nullable=False, server_default="USER"),
|
||||
sa.Column("can_read", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("can_write", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("can_manage", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("can_approve", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("page_scope", sa.String(length=50), nullable=True),
|
||||
sa.Column("created_at", sa.TIMESTAMP(), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")),
|
||||
sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"], name="fk_user_workspace_access_workspace"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint(
|
||||
"user_id",
|
||||
"tenant_id",
|
||||
"workspace_id",
|
||||
name="uq_user_workspace_access_user_tenant_workspace",
|
||||
),
|
||||
mysql_engine="InnoDB",
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"workspace_channel_mappings",
|
||||
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
||||
sa.Column("workspace_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("abc_channel_id", sa.String(length=100), nullable=False),
|
||||
sa.Column("abc_channel_key", sa.String(length=100), nullable=True),
|
||||
sa.Column("form_template_version", sa.String(length=30), nullable=True),
|
||||
sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("created_at", sa.TIMESTAMP(), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")),
|
||||
sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"], name="fk_workspace_channel_mappings_workspace"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("workspace_id", "abc_channel_id", name="uq_workspace_channel_mappings_workspace_channel"),
|
||||
mysql_engine="InnoDB",
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"workspace_field_mappings",
|
||||
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
||||
sa.Column("workspace_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("abc_channel_id", sa.String(length=100), nullable=False),
|
||||
sa.Column("abc_field_key", sa.String(length=100), nullable=False),
|
||||
sa.Column("local_field_code", sa.String(length=100), nullable=False),
|
||||
sa.Column("field_label", sa.String(length=100), nullable=False),
|
||||
sa.Column("field_type", sa.String(length=30), nullable=False),
|
||||
sa.Column("is_required", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("sort_order", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("validation_rule", sa.JSON(), nullable=True),
|
||||
sa.Column("created_at", sa.TIMESTAMP(), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")),
|
||||
sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"], name="fk_workspace_field_mappings_workspace"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint(
|
||||
"workspace_id",
|
||||
"abc_channel_id",
|
||||
"abc_field_key",
|
||||
name="uq_workspace_field_mappings_workspace_channel_field",
|
||||
),
|
||||
mysql_engine="InnoDB",
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"support_status_codes",
|
||||
sa.Column("code", sa.String(length=20), nullable=False),
|
||||
sa.Column("name", sa.String(length=50), nullable=False),
|
||||
sa.Column("sort_order", sa.Integer(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("code"),
|
||||
mysql_engine="InnoDB",
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"support_category_codes",
|
||||
sa.Column("code", sa.String(length=20), nullable=False),
|
||||
sa.Column("name", sa.String(length=50), nullable=False),
|
||||
sa.Column("sort_order", sa.Integer(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("code"),
|
||||
mysql_engine="InnoDB",
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"support_tickets",
|
||||
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
||||
sa.Column("workspace_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("requester_id", sa.String(length=100), nullable=False),
|
||||
sa.Column("requester_tenant_id", sa.String(length=100), nullable=False),
|
||||
sa.Column("ticket_type", sa.String(length=30), nullable=False),
|
||||
sa.Column("source_system", sa.String(length=30), nullable=False, server_default="ABC"),
|
||||
sa.Column("title", sa.String(length=255), nullable=False),
|
||||
sa.Column("category_code", sa.String(length=20), nullable=True),
|
||||
sa.Column("status_code", sa.String(length=20), nullable=False),
|
||||
sa.Column("is_secret", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("requires_approval", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("current_assignee_id", sa.String(length=100), nullable=True),
|
||||
sa.Column("current_assignee_tenant_id", sa.String(length=100), nullable=True),
|
||||
sa.Column("requested_start_at", sa.TIMESTAMP(), nullable=True),
|
||||
sa.Column("requested_end_at", sa.TIMESTAMP(), nullable=True),
|
||||
sa.Column("priority", sa.String(length=20), nullable=False, server_default="NORMAL"),
|
||||
sa.Column("created_at", sa.TIMESTAMP(), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.TIMESTAMP(),
|
||||
nullable=False,
|
||||
server_default=sa.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
|
||||
),
|
||||
sa.ForeignKeyConstraint(["category_code"], ["support_category_codes.code"], name="fk_support_tickets_category_code"),
|
||||
sa.ForeignKeyConstraint(["status_code"], ["support_status_codes.code"], name="fk_support_tickets_status_code"),
|
||||
sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"], name="fk_support_tickets_workspace"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
mysql_engine="InnoDB",
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"abc_feedback_mappings",
|
||||
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
||||
sa.Column("ticket_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("workspace_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("abc_channel_id", sa.String(length=100), nullable=False),
|
||||
sa.Column("abc_feedback_id", sa.String(length=100), nullable=False),
|
||||
sa.Column("abc_feedback_url", sa.Text(), nullable=True),
|
||||
sa.Column("sync_status", sa.String(length=20), nullable=False, server_default="SYNCED"),
|
||||
sa.Column("last_synced_at", sa.TIMESTAMP(), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")),
|
||||
sa.Column("created_at", sa.TIMESTAMP(), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")),
|
||||
sa.ForeignKeyConstraint(["ticket_id"], ["support_tickets.id"], name="fk_abc_feedback_mappings_ticket"),
|
||||
sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"], name="fk_abc_feedback_mappings_workspace"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("abc_feedback_id", name="uq_abc_feedback_mappings_feedback_id"),
|
||||
sa.UniqueConstraint("ticket_id", name="uq_abc_feedback_mappings_ticket_id"),
|
||||
mysql_engine="InnoDB",
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"request_approvals",
|
||||
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
||||
sa.Column("ticket_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("approver_id", sa.String(length=100), nullable=False),
|
||||
sa.Column("approver_tenant_id", sa.String(length=100), nullable=False),
|
||||
sa.Column("approval_status", sa.String(length=20), nullable=False),
|
||||
sa.Column("comment", sa.Text(), nullable=True),
|
||||
sa.Column("approved_at", sa.TIMESTAMP(), nullable=True),
|
||||
sa.Column("created_at", sa.TIMESTAMP(), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")),
|
||||
sa.ForeignKeyConstraint(["ticket_id"], ["support_tickets.id"], name="fk_request_approvals_ticket"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
mysql_engine="InnoDB",
|
||||
)
|
||||
|
||||
op.bulk_insert(
|
||||
support_status_codes,
|
||||
[
|
||||
{"code": "RECEIVED", "name": "접수", "sort_order": 10},
|
||||
{"code": "PENDING_APPROVAL", "name": "승인 대기", "sort_order": 20},
|
||||
{"code": "APPROVED", "name": "승인 완료", "sort_order": 30},
|
||||
{"code": "REJECTED", "name": "반려", "sort_order": 40},
|
||||
{"code": "IN_PROGRESS", "name": "처리 중", "sort_order": 50},
|
||||
{"code": "RESOLVED", "name": "처리 완료", "sort_order": 60},
|
||||
{"code": "CLOSED", "name": "종료", "sort_order": 70},
|
||||
],
|
||||
)
|
||||
|
||||
op.bulk_insert(
|
||||
support_category_codes,
|
||||
[
|
||||
{"code": "SUPPLIES", "name": "물품 신청", "sort_order": 10},
|
||||
{"code": "BOOK", "name": "도서 신청", "sort_order": 20},
|
||||
{"code": "VEHICLE", "name": "차량 신청", "sort_order": 30},
|
||||
{"code": "EQUIPMENT", "name": "비품 대여", "sort_order": 40},
|
||||
{"code": "GENERAL_QNA", "name": "일반 문의", "sort_order": 50},
|
||||
],
|
||||
)
|
||||
|
||||
op.bulk_insert(
|
||||
service_types,
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"service_code": "SUPPLIES_REQUEST",
|
||||
"service_name": "물품 신청",
|
||||
"description": "인트라넷 물품 신청 서비스",
|
||||
"is_active": True,
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"service_code": "BOOK_REQUEST",
|
||||
"service_name": "도서 신청",
|
||||
"description": "인트라넷 도서 신청 서비스",
|
||||
"is_active": True,
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"service_code": "VEHICLE_REQUEST",
|
||||
"service_name": "출장 차량 신청",
|
||||
"description": "인트라넷 차량 배차 신청 서비스",
|
||||
"is_active": True,
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"service_code": "EQUIPMENT_RENTAL",
|
||||
"service_name": "비품 대여",
|
||||
"description": "인트라넷 비품 대여 서비스",
|
||||
"is_active": True,
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"service_code": "GENERAL_QNA",
|
||||
"service_name": "사내 일반 문의",
|
||||
"description": "인트라넷 일반 문의 서비스",
|
||||
"is_active": True,
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
op.bulk_insert(
|
||||
workspaces,
|
||||
[
|
||||
{
|
||||
"workspace_type": "INTRANET_SERVICE",
|
||||
"software_app_id": None,
|
||||
"service_type_id": 1,
|
||||
"workspace_code": "INTRA_SUPPLIES_REQUEST",
|
||||
"workspace_name": "인트라넷 물품 신청",
|
||||
"is_active": True,
|
||||
},
|
||||
{
|
||||
"workspace_type": "INTRANET_SERVICE",
|
||||
"software_app_id": None,
|
||||
"service_type_id": 2,
|
||||
"workspace_code": "INTRA_BOOK_REQUEST",
|
||||
"workspace_name": "인트라넷 도서 신청",
|
||||
"is_active": True,
|
||||
},
|
||||
{
|
||||
"workspace_type": "INTRANET_SERVICE",
|
||||
"software_app_id": None,
|
||||
"service_type_id": 3,
|
||||
"workspace_code": "INTRA_VEHICLE_REQUEST",
|
||||
"workspace_name": "인트라넷 차량 신청",
|
||||
"is_active": True,
|
||||
},
|
||||
{
|
||||
"workspace_type": "INTRANET_SERVICE",
|
||||
"software_app_id": None,
|
||||
"service_type_id": 4,
|
||||
"workspace_code": "INTRA_EQUIPMENT_RENTAL",
|
||||
"workspace_name": "인트라넷 비품 대여",
|
||||
"is_active": True,
|
||||
},
|
||||
{
|
||||
"workspace_type": "INTRANET_SERVICE",
|
||||
"software_app_id": None,
|
||||
"service_type_id": 5,
|
||||
"workspace_code": "INTRA_GENERAL_QNA",
|
||||
"workspace_name": "인트라넷 일반 문의",
|
||||
"is_active": True,
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("request_approvals")
|
||||
op.drop_table("abc_feedback_mappings")
|
||||
op.drop_table("support_tickets")
|
||||
op.drop_table("support_category_codes")
|
||||
op.drop_table("support_status_codes")
|
||||
op.drop_table("workspace_field_mappings")
|
||||
op.drop_table("workspace_channel_mappings")
|
||||
op.drop_table("user_workspace_access")
|
||||
op.drop_table("workspaces")
|
||||
op.drop_table("service_types")
|
||||
op.drop_table("software_apps")
|
||||
@@ -0,0 +1,62 @@
|
||||
"""add support ticket content fields
|
||||
|
||||
Revision ID: 0002_support_fields
|
||||
Revises: 0001_initial_support_schema
|
||||
Create Date: 2026-07-10 00:00:00
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import inspect
|
||||
|
||||
revision = "0002_support_fields"
|
||||
down_revision = "0001_initial_support_schema"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = inspect(bind)
|
||||
existing_columns = {column["name"] for column in inspector.get_columns("support_tickets")}
|
||||
|
||||
if "requester_contact" not in existing_columns:
|
||||
op.add_column("support_tickets", sa.Column("requester_contact", sa.String(length=100), nullable=True))
|
||||
|
||||
if "description" not in existing_columns:
|
||||
op.add_column("support_tickets", sa.Column("description", sa.Text(), nullable=True))
|
||||
|
||||
if "approval_status" not in existing_columns:
|
||||
op.add_column(
|
||||
"support_tickets",
|
||||
sa.Column("approval_status", sa.String(length=20), nullable=False, server_default="NOT_REQUIRED"),
|
||||
)
|
||||
|
||||
if "sync_status" not in existing_columns:
|
||||
op.add_column(
|
||||
"support_tickets",
|
||||
sa.Column("sync_status", sa.String(length=20), nullable=False, server_default="PENDING"),
|
||||
)
|
||||
|
||||
if "issue_link_status" not in existing_columns:
|
||||
op.add_column(
|
||||
"support_tickets",
|
||||
sa.Column("issue_link_status", sa.String(length=20), nullable=False, server_default="NOT_REQUIRED"),
|
||||
)
|
||||
|
||||
if "extra_fields" not in existing_columns:
|
||||
op.add_column("support_tickets", sa.Column("extra_fields", sa.JSON(), nullable=True))
|
||||
|
||||
op.execute("UPDATE support_tickets SET description = '' WHERE description IS NULL")
|
||||
op.alter_column("support_tickets", "description", existing_type=sa.Text(), nullable=False)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_column("support_tickets", "extra_fields")
|
||||
op.drop_column("support_tickets", "issue_link_status")
|
||||
op.drop_column("support_tickets", "sync_status")
|
||||
op.drop_column("support_tickets", "approval_status")
|
||||
op.drop_column("support_tickets", "description")
|
||||
op.drop_column("support_tickets", "requester_contact")
|
||||
@@ -0,0 +1,204 @@
|
||||
"""add comment, attachment, and approval workflow tables
|
||||
|
||||
Revision ID: 0003_support_comment_flow
|
||||
Revises: 0002_support_fields
|
||||
Create Date: 2026-07-13 00:00:00
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import inspect
|
||||
|
||||
revision = "0003_support_comment_flow"
|
||||
down_revision = "0002_support_fields"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
inspector = inspect(bind)
|
||||
existing_tables = set(inspector.get_table_names())
|
||||
|
||||
if "workspace_approval_policies" not in existing_tables:
|
||||
op.create_table(
|
||||
"workspace_approval_policies",
|
||||
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
||||
sa.Column("workspace_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("request_category_code", sa.String(length=50), nullable=True),
|
||||
sa.Column("ticket_type", sa.String(length=30), nullable=True),
|
||||
sa.Column("rule_name", sa.String(length=100), nullable=False),
|
||||
sa.Column("description", sa.Text(), nullable=True),
|
||||
sa.Column("conditions", sa.JSON(), nullable=True),
|
||||
sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("version", sa.Integer(), nullable=False, server_default="1"),
|
||||
sa.Column("created_by", sa.String(length=100), nullable=True),
|
||||
sa.Column("created_at", sa.TIMESTAMP(), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.TIMESTAMP(),
|
||||
nullable=False,
|
||||
server_default=sa.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
|
||||
),
|
||||
sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"], name="fk_workspace_approval_policies_workspace"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint(
|
||||
"workspace_id",
|
||||
"rule_name",
|
||||
"version",
|
||||
name="uq_workspace_approval_policies_workspace_rule_version",
|
||||
),
|
||||
mysql_engine="InnoDB",
|
||||
)
|
||||
|
||||
if "workspace_approval_steps" not in existing_tables:
|
||||
op.create_table(
|
||||
"workspace_approval_steps",
|
||||
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
||||
sa.Column("policy_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("step_order", sa.Integer(), nullable=False),
|
||||
sa.Column("approver_type", sa.String(length=30), nullable=False),
|
||||
sa.Column("approver_key", sa.String(length=100), nullable=False),
|
||||
sa.Column("approver_name", sa.String(length=100), nullable=True),
|
||||
sa.Column("approval_mode", sa.String(length=20), nullable=False, server_default="ALL"),
|
||||
sa.Column("is_required", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("sla_hours", sa.Integer(), nullable=True),
|
||||
sa.Column("created_at", sa.TIMESTAMP(), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")),
|
||||
sa.ForeignKeyConstraint(["policy_id"], ["workspace_approval_policies.id"], name="fk_workspace_approval_steps_policy"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("policy_id", "step_order", name="uq_workspace_approval_steps_policy_order"),
|
||||
mysql_engine="InnoDB",
|
||||
)
|
||||
|
||||
if "ticket_comments" not in existing_tables:
|
||||
op.create_table(
|
||||
"ticket_comments",
|
||||
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
||||
sa.Column("ticket_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("parent_comment_id", sa.BigInteger(), nullable=True),
|
||||
sa.Column("author_id", sa.String(length=100), nullable=False),
|
||||
sa.Column("author_tenant_id", sa.String(length=100), nullable=False),
|
||||
sa.Column("author_name", sa.String(length=100), nullable=True),
|
||||
sa.Column("comment_type", sa.String(length=20), nullable=False, server_default="COMMENT"),
|
||||
sa.Column("content", sa.Text(), nullable=False),
|
||||
sa.Column("is_internal", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("abc_comment_id", sa.String(length=100), nullable=True),
|
||||
sa.Column("sync_status", sa.String(length=20), nullable=False, server_default="PENDING"),
|
||||
sa.Column("edited_at", sa.TIMESTAMP(), nullable=True),
|
||||
sa.Column("created_at", sa.TIMESTAMP(), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.TIMESTAMP(),
|
||||
nullable=False,
|
||||
server_default=sa.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
|
||||
),
|
||||
sa.Column("deleted_at", sa.TIMESTAMP(), nullable=True),
|
||||
sa.ForeignKeyConstraint(["ticket_id"], ["support_tickets.id"], name="fk_ticket_comments_ticket"),
|
||||
sa.ForeignKeyConstraint(["parent_comment_id"], ["ticket_comments.id"], name="fk_ticket_comments_parent_comment"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("abc_comment_id", name="uq_ticket_comments_abc_comment_id"),
|
||||
mysql_engine="InnoDB",
|
||||
)
|
||||
op.create_index("idx_ticket_comments_ticket_created", "ticket_comments", ["ticket_id", "created_at"])
|
||||
|
||||
if "attachments" not in existing_tables:
|
||||
op.create_table(
|
||||
"attachments",
|
||||
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
||||
sa.Column("ticket_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("comment_id", sa.BigInteger(), nullable=True),
|
||||
sa.Column("workspace_id", sa.BigInteger(), nullable=False),
|
||||
sa.Column("uploader_id", sa.String(length=100), nullable=False),
|
||||
sa.Column("uploader_tenant_id", sa.String(length=100), nullable=False),
|
||||
sa.Column("original_file_name", sa.String(length=255), nullable=False),
|
||||
sa.Column("stored_file_name", sa.String(length=255), nullable=True),
|
||||
sa.Column("storage_provider", sa.String(length=30), nullable=False, server_default="LOCAL"),
|
||||
sa.Column("storage_bucket", sa.String(length=255), nullable=True),
|
||||
sa.Column("storage_key", sa.String(length=500), nullable=False),
|
||||
sa.Column("mime_type", sa.String(length=100), nullable=True),
|
||||
sa.Column("file_extension", sa.String(length=20), nullable=True),
|
||||
sa.Column("file_size", sa.BigInteger(), nullable=True),
|
||||
sa.Column("checksum_sha256", sa.String(length=64), nullable=True),
|
||||
sa.Column("abc_attachment_id", sa.String(length=100), nullable=True),
|
||||
sa.Column("attachment_status", sa.String(length=20), nullable=False, server_default="ACTIVE"),
|
||||
sa.Column("created_at", sa.TIMESTAMP(), nullable=False, server_default=sa.text("CURRENT_TIMESTAMP")),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.TIMESTAMP(),
|
||||
nullable=False,
|
||||
server_default=sa.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
|
||||
),
|
||||
sa.Column("deleted_at", sa.TIMESTAMP(), nullable=True),
|
||||
sa.CheckConstraint(
|
||||
"comment_id IS NULL OR ticket_id IS NOT NULL",
|
||||
name="chk_attachments_comment_requires_ticket",
|
||||
),
|
||||
sa.ForeignKeyConstraint(["comment_id"], ["ticket_comments.id"], name="fk_attachments_comment"),
|
||||
sa.ForeignKeyConstraint(["ticket_id"], ["support_tickets.id"], name="fk_attachments_ticket"),
|
||||
sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"], name="fk_attachments_workspace"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("storage_provider", "storage_key", name="uq_attachments_storage_provider_key"),
|
||||
sa.UniqueConstraint("abc_attachment_id", name="uq_attachments_abc_attachment_id"),
|
||||
mysql_engine="InnoDB",
|
||||
)
|
||||
op.create_index("idx_attachments_ticket_created", "attachments", ["ticket_id", "created_at"])
|
||||
op.create_index("idx_attachments_comment_created", "attachments", ["comment_id", "created_at"])
|
||||
|
||||
approval_columns = {column["name"] for column in inspector.get_columns("request_approvals")}
|
||||
|
||||
if "workflow_policy_id" not in approval_columns:
|
||||
op.add_column("request_approvals", sa.Column("workflow_policy_id", sa.BigInteger(), nullable=True))
|
||||
op.create_foreign_key(
|
||||
"fk_request_approvals_workflow_policy",
|
||||
"request_approvals",
|
||||
"workspace_approval_policies",
|
||||
["workflow_policy_id"],
|
||||
["id"],
|
||||
)
|
||||
|
||||
if "workflow_step_id" not in approval_columns:
|
||||
op.add_column("request_approvals", sa.Column("workflow_step_id", sa.BigInteger(), nullable=True))
|
||||
op.create_foreign_key(
|
||||
"fk_request_approvals_workflow_step",
|
||||
"request_approvals",
|
||||
"workspace_approval_steps",
|
||||
["workflow_step_id"],
|
||||
["id"],
|
||||
)
|
||||
|
||||
if "step_order" not in approval_columns:
|
||||
op.add_column("request_approvals", sa.Column("step_order", sa.Integer(), nullable=True))
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
approval_columns = {column["name"] for column in inspect(op.get_bind()).get_columns("request_approvals")}
|
||||
|
||||
if "step_order" in approval_columns:
|
||||
op.drop_column("request_approvals", "step_order")
|
||||
|
||||
if "workflow_step_id" in approval_columns:
|
||||
op.drop_constraint("fk_request_approvals_workflow_step", "request_approvals", type_="foreignkey")
|
||||
op.drop_column("request_approvals", "workflow_step_id")
|
||||
|
||||
if "workflow_policy_id" in approval_columns:
|
||||
op.drop_constraint("fk_request_approvals_workflow_policy", "request_approvals", type_="foreignkey")
|
||||
op.drop_column("request_approvals", "workflow_policy_id")
|
||||
|
||||
existing_tables = set(inspect(op.get_bind()).get_table_names())
|
||||
|
||||
if "attachments" in existing_tables:
|
||||
op.drop_index("idx_attachments_comment_created", table_name="attachments")
|
||||
op.drop_index("idx_attachments_ticket_created", table_name="attachments")
|
||||
op.drop_table("attachments")
|
||||
|
||||
if "ticket_comments" in existing_tables:
|
||||
op.drop_index("idx_ticket_comments_ticket_created", table_name="ticket_comments")
|
||||
op.drop_table("ticket_comments")
|
||||
|
||||
if "workspace_approval_steps" in existing_tables:
|
||||
op.drop_table("workspace_approval_steps")
|
||||
|
||||
if "workspace_approval_policies" in existing_tables:
|
||||
op.drop_table("workspace_approval_policies")
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user