Full CRUD on Participant scoped to the caller's organization via new get_route_in_organization/get_participant_in_organization helpers in app/dependencies.py (shared cross-cutting spot, reused by task 6). PATCH is_active is the primary deactivation path — replaces the old spreadsheet-era "empty order" trick and preserves order history for participants who leave and later return. DELETE is a separate, superadmin-gated hard-delete for GDPR erasure requests, which cascades to the participant's orders. Adds UNIQUE(route_id, email) at the DB level (existing 308 participants already conform, per task 3's dedup) plus email normalization on create/update, so case-variant duplicates can't reappear via the API. Every write path relies on catching the constraint's IntegrityError for a clean 409 rather than a racy pre-check. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
33 lines
987 B
Python
33 lines
987 B
Python
"""add unique constraint on participant route_id and email
|
|
|
|
Revision ID: ddf4dfafdc62
|
|
Revises: 237700c56d84
|
|
Create Date: 2026-09-28 13:04:02.903665
|
|
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
import sqlmodel
|
|
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision: str = 'ddf4dfafdc62'
|
|
down_revision: Union[str, Sequence[str], None] = '237700c56d84'
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
"""Upgrade schema."""
|
|
# ### commands auto generated by Alembic - please adjust! ###
|
|
op.create_unique_constraint('uq_participant_route_email', 'participant', ['route_id', 'email'])
|
|
# ### end Alembic commands ###
|
|
|
|
|
|
def downgrade() -> None:
|
|
"""Downgrade schema."""
|
|
# ### commands auto generated by Alembic - please adjust! ###
|
|
op.drop_constraint('uq_participant_route_email', 'participant', type_='unique')
|
|
# ### end Alembic commands ###
|