Prepare ERP source for Gitea deployment
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
"""Phase 7S.3 email-to-engagement mapping
|
||||
|
||||
Revision ID: 20260612_phase_7s3_email_mapping
|
||||
Revises: 20260611_phase_7s2_imap_incoming
|
||||
Create Date: 2026-06-12
|
||||
|
||||
SQLite-safe revision:
|
||||
- Adds reference id columns as plain Integer columns instead of adding
|
||||
ForeignKey constraints through ALTER TABLE, because SQLite cannot ALTER
|
||||
constraints without batch copy/move migration.
|
||||
- SQLAlchemy model relationships can still use these id columns for lookups.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision = "20260612_phase_7s3_email_mapping"
|
||||
down_revision = "20260611_phase_7s2_imap_incoming"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def _inspector():
|
||||
return sa.inspect(op.get_bind())
|
||||
|
||||
|
||||
def _has_table(table_name: str) -> bool:
|
||||
return table_name in _inspector().get_table_names()
|
||||
|
||||
|
||||
def _has_column(table_name: str, column_name: str) -> bool:
|
||||
if not _has_table(table_name):
|
||||
return False
|
||||
return column_name in {col["name"] for col in _inspector().get_columns(table_name)}
|
||||
|
||||
|
||||
def _create_index_if_missing(table_name: str, index_name: str, columns: list[str]) -> None:
|
||||
if not _has_table(table_name):
|
||||
return
|
||||
existing = {idx["name"] for idx in _inspector().get_indexes(table_name)}
|
||||
if index_name not in existing:
|
||||
op.create_index(index_name, table_name, columns)
|
||||
|
||||
|
||||
def _add_column_if_missing(table_name: str, column: sa.Column) -> None:
|
||||
if _has_table(table_name) and not _has_column(table_name, column.name):
|
||||
op.add_column(table_name, column)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
if not _has_table("email_incoming_messages"):
|
||||
return
|
||||
|
||||
_add_column_if_missing(
|
||||
"email_incoming_messages",
|
||||
sa.Column("tracking_code", sa.String(length=80), nullable=True),
|
||||
)
|
||||
|
||||
# SQLite does not support ALTER TABLE ADD CONSTRAINT for ForeignKey.
|
||||
# Keep these as nullable integer reference columns. Application code and
|
||||
# SQLAlchemy models can still resolve the linked records safely.
|
||||
_add_column_if_missing(
|
||||
"email_incoming_messages",
|
||||
sa.Column("matched_engagement_id", sa.Integer(), nullable=True),
|
||||
)
|
||||
_add_column_if_missing(
|
||||
"email_incoming_messages",
|
||||
sa.Column("matched_task_id", sa.Integer(), nullable=True),
|
||||
)
|
||||
_add_column_if_missing(
|
||||
"email_incoming_messages",
|
||||
sa.Column("matched_invoice_id", sa.Integer(), nullable=True),
|
||||
)
|
||||
_add_column_if_missing(
|
||||
"email_incoming_messages",
|
||||
sa.Column("mapping_status", sa.String(length=30), nullable=False, server_default="UNMAPPED"),
|
||||
)
|
||||
_add_column_if_missing(
|
||||
"email_incoming_messages",
|
||||
sa.Column("mapping_notes", sa.Text(), nullable=True),
|
||||
)
|
||||
_add_column_if_missing(
|
||||
"email_incoming_messages",
|
||||
sa.Column("mapped_at_utc", sa.DateTime(timezone=True), nullable=True),
|
||||
)
|
||||
|
||||
for name, cols in {
|
||||
"ix_email_incoming_messages_tracking_code": ["tracking_code"],
|
||||
"ix_email_incoming_messages_matched_engagement_id": ["matched_engagement_id"],
|
||||
"ix_email_incoming_messages_matched_task_id": ["matched_task_id"],
|
||||
"ix_email_incoming_messages_matched_invoice_id": ["matched_invoice_id"],
|
||||
"ix_email_incoming_messages_mapping_status": ["mapping_status"],
|
||||
"ix_email_incoming_messages_mapped_at_utc": ["mapped_at_utc"],
|
||||
}.items():
|
||||
_create_index_if_missing("email_incoming_messages", name, cols)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Keep defensive because SQLite cannot reliably drop columns/constraints
|
||||
# in all local development environments without a batch table rewrite.
|
||||
pass
|
||||
Reference in New Issue
Block a user