Files
JQC_multi_tenant/migrations/versions/phase37_contract_recipients.py
T

61 lines
2.4 KiB
Python

"""phase37 — per-contract additional notification recipients
Creates the project_notification_recipients table. Each row subscribes one
recipient — either a staff User (in-app + email) or an external email address
(email only) — to a chosen set of notification-matrix event types, scoped to
events occurring in facilities that belong to that contract (Project).
Dispatched by notify_by_matrix() → _notify_project_recipients() in
app/utils/notifications.py, AFTER the global matrix roles and custom emails.
Idempotent: guarded by an INFORMATION_SCHEMA table-existence check so it is
safe to re-run across every tenant DB (CLAUDE.md rule 14).
NOTE: the revision id is deliberately short — alembic_version.version_num is
VARCHAR(32); ids longer than 32 chars fail the version-pointer UPDATE with
MySQL error 1406 ("Data too long for column 'version_num'").
"""
import sqlalchemy as sa
from alembic import op
revision = 'phase37_contract_recipients'
down_revision = 'phase36_issue_work_orders'
branch_labels = None
depends_on = None
def _table_exists(bind, table: str) -> bool:
result = bind.execute(sa.text(
"SELECT COUNT(*) FROM information_schema.tables "
"WHERE table_schema = DATABASE() AND table_name = :t"
), {'t': table})
return result.scalar() > 0
def upgrade():
bind = op.get_bind()
if not _table_exists(bind, 'project_notification_recipients'):
op.execute(sa.text("""
CREATE TABLE project_notification_recipients (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
project_id INT NOT NULL,
user_id INT NULL,
email VARCHAR(255) NULL,
events TEXT NOT NULL,
created_at DATETIME NOT NULL,
CONSTRAINT fk_pnr_project FOREIGN KEY (project_id)
REFERENCES projects(id) ON DELETE CASCADE,
CONSTRAINT fk_pnr_user FOREIGN KEY (user_id)
REFERENCES users(id) ON DELETE CASCADE,
INDEX ix_pnr_project (project_id),
INDEX ix_pnr_user (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
"""))
def downgrade():
bind = op.get_bind()
if _table_exists(bind, 'project_notification_recipients'):
op.execute(sa.text('DROP TABLE project_notification_recipients'))