Aug 17 - Update customer roles management

This commit is contained in:
2026-08-17 14:18:10 -04:00
parent 3209a0c717
commit 5486145a24
24 changed files with 1119 additions and 191 deletions
@@ -0,0 +1,63 @@
"""phase51 — per-account notification overrides
Creates `user_notification_matrix`, the per-account layer over the global
NotificationMatrix. One row = one account's explicit answer for one event
(enabled True/False); NO row means "inherit the global column".
See app/models/user_notification_matrix.py for the semantics.
**No backfill, deliberately.** An empty table means every account inherits,
which is exactly today's behaviour — so this migration cannot change who gets
notified. Overrides are created only when an admin sets one on the account's
page in Customer Management. Backfilling from the current global matrix would
freeze every account at today's routing and quietly break future changes to the
global columns.
The rest of phase51 (Customer Director / Customer Inspector) is a LABEL-only
rename over the existing 'customer' and 'external_inspector' ENUM values, so
there is no ENUM change and no user row is touched here.
Table-existence check — safe to re-run.
"""
revision = 'phase51_user_notif_matrix'
down_revision = 'phase50_default_modern'
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
def _has_table(conn, name):
return conn.execute(sa.text(
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES "
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :t"
), {'t': name}).scalar() > 0
def upgrade():
conn = op.get_bind()
if _has_table(conn, 'user_notification_matrix'):
return
op.create_table(
'user_notification_matrix',
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('user_id', sa.Integer,
sa.ForeignKey('users.id', ondelete='CASCADE'),
nullable=False, index=True),
sa.Column('event_type', sa.String(50), nullable=False),
sa.Column('enabled', sa.Boolean, nullable=False,
server_default=sa.text('1')),
sa.UniqueConstraint('user_id', 'event_type',
name='uq_user_notif_matrix_user_event'),
)
def downgrade():
conn = op.get_bind()
if _has_table(conn, 'user_notification_matrix'):
# Every row here is an explicit admin decision; dropping the table
# discards them and returns all accounts to global-matrix routing.
op.drop_table('user_notification_matrix')