Files
zkxw/backend/app/services/project_assignment.py
zlwu bd62416263 feat(project): add team attribution and business statistics
Add tenant-scoped teams and projects, order attribution across four businesses, conversion inheritance, scoped statistics and audited reassignment. Reuse existing Web and mobile UI patterns; team regions and hospitals remain informational.

Include incremental SQL and isolated regression coverage. Actual MySQL and browser/WebView integration validation remains pending; relates to issue #13.
2026-11-02 15:29:43 +08:00

52 lines
2.2 KiB
Python

"""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