from __future__ import annotations from sqlalchemy import select from sqlalchemy.orm import Session from app.modules.core.iam.models import User from app.modules.core.iam.profile_service import profile_photo_url from app.modules.core.tenancy.models import Branch, Tenant def _initials(name: str | None, email: str | None = None) -> str: source = (name or email or "Auditor").strip() parts = [p for p in source.replace("@", " ").replace(".", " ").split() if p] if not parts: return "AU" if len(parts) == 1: return parts[0][:2].upper() return (parts[0][:1] + parts[-1][:1]).upper() def _contact_card_from_user( *, user: User | None, tenant_name: str | None, branch_name: str | None, source_label: str, ) -> dict: if not user: return { "available": False, "name": "Firm team", "designation": "Audit support team", "qualification": None, "email": None, "mobile": None, "photo_url": None, "initials": "FT", "firm_name": tenant_name, "branch_name": branch_name, "source_label": source_label, } name = getattr(user, "full_name", None) or getattr(user, "email", None) or "Firm team" designation = getattr(user, "designation", None) or source_label or "Auditor" return { "available": True, "name": name, "designation": designation, "qualification": getattr(user, "qualification", None), "email": getattr(user, "email", None), "mobile": getattr(user, "mobile", None), "photo_url": profile_photo_url(user), "initials": _initials(name, getattr(user, "email", None)), "firm_name": tenant_name, "branch_name": branch_name, "source_label": source_label, } def build_client_auditor_card(db: Session, client_row: dict | None) -> dict: """Return a client-facing contact card for the assigned auditor/partner. Priority: 1. Client assigned partner (`partner_id`). 2. Default review partner, if no assigned partner exists. 3. Firm team fallback using tenant/branch names. This reuses Phase 7Q.3 user profile fields and does not create new tables. """ if not client_row: return _contact_card_from_user( user=None, tenant_name=None, branch_name=None, source_label="Firm team", ) tenant_name = client_row.get("tenant_name") branch_name = client_row.get("branch_name") tenant_id = client_row.get("tenant_id") branch_id = client_row.get("branch_id") partner_id = client_row.get("partner_id") or client_row.get("assoc_partner_user_id") review_partner_id = client_row.get("default_review_partner_user_id") target_user_id = partner_id or review_partner_id source_label = "Assigned Auditor" if partner_id else "Review Partner" if not target_user_id: return _contact_card_from_user( user=None, tenant_name=tenant_name, branch_name=branch_name, source_label="Firm team", ) stmt = ( select(User, Tenant.name.label("tenant_name"), Branch.name.label("branch_name")) .join(Tenant, Tenant.id == User.tenant_id, isouter=True) .join(Branch, Branch.id == User.branch_id, isouter=True) .where(User.id == int(target_user_id), User.deleted_at.is_(None)) ) if tenant_id: stmt = stmt.where(User.tenant_id == int(tenant_id)) result = db.execute(stmt).first() if not result: return _contact_card_from_user( user=None, tenant_name=tenant_name, branch_name=branch_name, source_label="Firm team", ) user, resolved_tenant_name, resolved_branch_name = result return _contact_card_from_user( user=user, tenant_name=resolved_tenant_name or tenant_name, branch_name=resolved_branch_name or branch_name, source_label=source_label, )