84 lines
2.7 KiB
Python
84 lines
2.7 KiB
Python
"""phase50 — inspection schedule receipt acknowledgement
|
|
|
|
Ports single-tenant phase47 onto MT's `inspection_schedules` table. Adds:
|
|
|
|
acknowledged_at DATETIME NULL
|
|
|
|
Stamped when the assigned inspector confirms they have received/seen a scheduled
|
|
inspection request. NULL = awaiting confirmation.
|
|
|
|
Scope of the acknowledgement
|
|
----------------------------
|
|
It is per ASSIGNMENT, not per occurrence. `fulfill()` and `advance_due_date()`
|
|
deliberately leave this column alone as the schedule rolls forward — an
|
|
inspector who confirmed "yes, this weekly restroom round is mine" should not be
|
|
asked again every week. The edit route resets it to NULL when the schedule is
|
|
reassigned to a DIFFERENT inspector, because the new assignee has confirmed
|
|
nothing.
|
|
|
|
The acknowledger is always the assigned inspector — the only person the routes
|
|
permit to confirm — so no separate `acknowledged_by` column is stored.
|
|
|
|
Only meaningful for `plan` mode: an `auto` schedule materialises its inspection
|
|
without anyone starting it, so there is no request to receive. The UI shows the
|
|
control for plan-mode rows only.
|
|
|
|
No backfill: legacy rows keep NULL and render as "awaiting confirmation", which
|
|
is the correct initial state for an assignment nobody has confirmed yet.
|
|
|
|
Revision id note
|
|
----------------
|
|
`alembic_version.version_num` is VARCHAR(32); the id below is 24 characters.
|
|
The filename stays descriptive — Alembic keys on the `revision` string.
|
|
|
|
Uses an INFORMATION_SCHEMA check — safe to re-run on every tenant DB. Additive
|
|
only: nothing is renamed, retyped or dropped.
|
|
"""
|
|
|
|
revision = 'phase50_sched_acknowledged'
|
|
down_revision = 'phase49_followup_req_by'
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
|
|
_TABLE = 'inspection_schedules'
|
|
_COLUMN = 'acknowledged_at'
|
|
|
|
|
|
def _table_exists(conn, table):
|
|
return conn.execute(sa.text(
|
|
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES "
|
|
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :t"
|
|
), {"t": table}).scalar() > 0
|
|
|
|
|
|
def _column_exists(conn, table, column):
|
|
return conn.execute(sa.text(
|
|
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS "
|
|
"WHERE TABLE_SCHEMA = DATABASE() "
|
|
"AND TABLE_NAME = :t AND COLUMN_NAME = :c"
|
|
), {"t": table, "c": column}).scalar() > 0
|
|
|
|
|
|
def upgrade():
|
|
bind = op.get_bind()
|
|
if not _table_exists(bind, _TABLE):
|
|
return
|
|
if not _column_exists(bind, _TABLE, _COLUMN):
|
|
op.execute(sa.text(
|
|
f"ALTER TABLE {_TABLE} ADD COLUMN {_COLUMN} DATETIME NULL"
|
|
))
|
|
|
|
|
|
def downgrade():
|
|
bind = op.get_bind()
|
|
if not _table_exists(bind, _TABLE):
|
|
return
|
|
if _column_exists(bind, _TABLE, _COLUMN):
|
|
op.execute(sa.text(
|
|
f"ALTER TABLE {_TABLE} DROP COLUMN {_COLUMN}"
|
|
))
|