"""Shared attribution boundary for existing business write and read paths.""" from fastapi import HTTPException from app.core.config import settings def create_assignment(db, tenant_no, user, project_id, biz_type): from app.services.project_management import validate_assignment if biz_type == 'clinical_application': if project_id: raise HTTPException(400, '临床应用不支持项目归属') return None return validate_assignment(db, tenant_no, user, project_id, biz_type, required=settings.PROJECT_ASSIGNMENT_REQUIRED) def guard_project_edit(row, payload): """Omission preserves attribution; explicit changes require audited maintenance.""" key = 'projectId' if 'projectId' in payload else 'project_id' if key in payload and (payload[key] or None) != (row.project_id or None): raise HTTPException(400, '请通过项目归属维护调整') def assignment_brief(db, tenant_no, project_id): if not db or not project_id: return dict(projectId=project_id, projectName=None, teamId=None, teamName=None) from app.services.project_management import project_brief return project_brief(db, tenant_no, project_id) def filter_assignment(query, model, db, tenant_no, project_id=None, team_id=None): if project_id: query = query.filter(model.project_id == project_id) if team_id: from app.models.project_management import Project projects = db.query(Project.id).filter(Project.tenant_no == tenant_no, Project.team_id == team_id, Project.del_flag == 0) query = query.filter(model.project_id.in_(projects)) return query def accessible_order(db, model, tenant_no, user, record_id, *, lock=False): from app.core.data_scope import apply_data_scope query = db.query(model).filter(model.id == record_id, model.tenant_no == tenant_no, model.del_flag == 0) query = apply_data_scope(query, model, user, db, tenant_no) if lock: query = query.populate_existing().with_for_update() row = query.first() if row is None: raise HTTPException(404, '单据不存在或无权访问') return row