Aug 27 - Update code to follow-up with ST functions

This commit is contained in:
2026-08-27 11:54:07 -04:00
parent 3bfd81c84c
commit 2d68bad966
10 changed files with 617 additions and 16 deletions
+58
View File
@@ -207,6 +207,25 @@ class Inspection(db.Model):
)
follow_up_requested_at = db.Column(db.DateTime, nullable=True)
# phase56 — who is to PERFORM the follow-up re-inspection.
#
# NULL keeps the original behaviour: the follow-up belongs to the
# inspection's own inspector. When set, that person owns it instead — they
# are the one notified, and the one it appears for on the iPad. Lets a
# director (or a Customer Director) hand a re-inspection to someone other
# than whoever did the original.
#
# This is the THIRD FK from inspections to users: every relationship
# spanning the two must pin foreign_keys explicitly, or the mapper is
# ambiguous and blows up on first ORM USE rather than at import — the app
# starts cleanly and then every request 500s.
follow_up_assigned_to = db.Column(
db.Integer,
db.ForeignKey('users.id', ondelete='SET NULL',
name='fk_inspections_followup_assignee'),
nullable=True,
)
results = db.relationship('InspectionResult', backref='inspection', lazy='dynamic', cascade='all, delete-orphan')
issues = db.relationship('Issue', backref='inspection', lazy='dynamic', cascade='all, delete-orphan')
follow_ups = db.relationship('Inspection', backref=db.backref('parent', remote_side='Inspection.id'),
@@ -215,6 +234,45 @@ class Inspection(db.Model):
# users.id, so SQLAlchemy cannot infer which column this relationship uses.
follow_up_requester = db.relationship('User',
foreign_keys=[follow_up_requested_by])
follow_up_assignee = db.relationship('User',
foreign_keys=[follow_up_assigned_to])
@property
def follow_up_owner(self):
"""Who is expected to carry out the follow-up.
The explicit assignee when one is set, otherwise the inspection's own
inspector — the single definition of ownership, so the web display, the
notification and the mobile API filter cannot disagree about who owns a
follow-up.
"""
return self.follow_up_assignee or self.inspector
@staticmethod
def follow_up_owned_by(user_id):
"""SQL predicate: *user_id* owns this inspection's follow-up.
The query-side mirror of `follow_up_owner` above. Ownership has to be
expressed twice — once for a loaded row, once in SQL — so both live
here, together, and every caller uses one of them.
The two arms are mutually exclusive on purpose. Drop the `is_(None)`
from the second and an inspector keeps matching a follow-up that was
handed to someone else: two people turn up for the same re-inspection.
Callers: the mobile list filter, the web dashboard card, and the iPad
stats KPI. They previously each wrote their own version, and three of
them tested AUTHORSHIP — so an assignee saw the work in their list but
a 0 on both dashboards.
"""
return db.or_(
Inspection.follow_up_assigned_to == user_id,
db.and_(
Inspection.follow_up_assigned_to.is_(None),
Inspection.inspector_id == user_id,
),
)
# The schedule this inspection was started from / materialised by, so the
# detail view can show the cadence and who set it up. Explicit foreign_keys
# again: inspection_schedules.parent_inspection_id points back here (phase48),