Show trade name in Staff My Work

This commit is contained in:
A R R R Associates
2026-08-07 16:56:32 +05:30
parent 765833ce1b
commit 1ce4e3f21d
+270 -6
View File
@@ -21,9 +21,10 @@ from app.modules.core.rbac.models import Role, UserRole
from app.modules.core.tenancy.models import Branch, Tenant
from app.modules.core.tenancy.settings_models import BranchSettings
from app.modules.employees.models import Employee, EmployeeAttendance, EmployeeRegistrationRequest, EmployeeLeaveType, EmployeeLeaveBalance, EmployeeLeaveRequest, EmployeeDocumentType, EmployeeDocument, EmployeeOnboardingChecklistItem, EmployeeOnboardingTask, EmployeeOffboardingRequest, EmployeeOffboardingTask, EmployeeSalaryStructure, EmployeePayrollRun, EmployeePayslip
from app.modules.clients.models import Client
from app.modules.clients.models import Client, ClientBusinessUnit, ClientBranch
from app.modules.documents.models import EngagementDocument
from app.modules.services.models import ClientServiceTaskInstance, ClientServiceSubscription, ServiceCatalogue, ServiceTaskComment
from app.modules.registrations.models import ClientRegistration, RegistrationType
from app.modules.services.execution import (
CLOSED_TASK_STATUSES,
TASK_PRIORITIES,
@@ -2868,6 +2869,240 @@ def resume_employee_engagement_workflow(db: Session, scope: EmployeeScope, engag
db.commit(); db.refresh(subscription); return subscription
def _employee_work_scope_display_map(
db: Session,
*,
tenant_id: int,
subscriptions: list[ClientServiceSubscription],
) -> dict[int, dict[str, str]]:
"""Resolve the display identity for Employee My Work without changing ownership.
Display priority:
- registration scope: registration trade name -> business-unit trade/name ->
branch name -> registration legal name -> client trade name -> client name
- business-unit scope: business-unit trade/name -> client trade name -> client name
- client-branch scope: branch name -> business-unit trade/name -> client trade/name
- client scope: client trade name -> client name
The legal client remains available as context for scoped engagements.
"""
subscriptions = [row for row in subscriptions if row is not None]
if not subscriptions:
return {}
business_ids = {
int(row.business_unit_id)
for row in subscriptions
if getattr(row, "business_unit_id", None)
}
branch_ids = {
int(row.client_branch_id)
for row in subscriptions
if getattr(row, "client_branch_id", None)
}
registration_ids = {
int(row.registration_id)
for row in subscriptions
if getattr(row, "registration_id", None)
}
businesses: dict[int, ClientBusinessUnit] = {}
if business_ids:
businesses = {
row.id: row
for row in db.execute(
select(ClientBusinessUnit).where(
ClientBusinessUnit.tenant_id == tenant_id,
ClientBusinessUnit.id.in_(business_ids),
)
).scalars().all()
}
branches: dict[int, ClientBranch] = {}
if branch_ids:
branches = {
row.id: row
for row in db.execute(
select(ClientBranch).where(
ClientBranch.tenant_id == tenant_id,
ClientBranch.id.in_(branch_ids),
)
).scalars().all()
}
registrations: dict[int, ClientRegistration] = {}
registration_types: dict[int, RegistrationType] = {}
if registration_ids:
for registration, registration_type in db.execute(
select(ClientRegistration, RegistrationType)
.join(
RegistrationType,
RegistrationType.id == ClientRegistration.registration_type_id,
)
.where(
ClientRegistration.tenant_id == tenant_id,
ClientRegistration.id.in_(registration_ids),
)
).all():
registrations[registration.id] = registration
registration_types[registration.id] = registration_type
# Registration scope can carry business/branch references even when the
# engagement's denormalised scope columns are blank.
extra_business_ids = {
int(row.business_unit_id)
for row in registrations.values()
if row.business_unit_id and row.business_unit_id not in businesses
}
if extra_business_ids:
businesses.update({
row.id: row
for row in db.execute(
select(ClientBusinessUnit).where(
ClientBusinessUnit.tenant_id == tenant_id,
ClientBusinessUnit.id.in_(extra_business_ids),
)
).scalars().all()
})
extra_branch_ids = {
int(row.client_branch_id)
for row in registrations.values()
if row.client_branch_id and row.client_branch_id not in branches
}
if extra_branch_ids:
branches.update({
row.id: row
for row in db.execute(
select(ClientBranch).where(
ClientBranch.tenant_id == tenant_id,
ClientBranch.id.in_(extra_branch_ids),
)
).scalars().all()
})
result: dict[int, dict[str, str]] = {}
for subscription in subscriptions:
client = getattr(subscription, "client", None)
client_name = (getattr(client, "client_name", None) or "").strip()
client_trade_name = (getattr(client, "trade_name", None) or "").strip()
client_code = (getattr(client, "client_code", None) or "").strip()
primary = client_trade_name or client_name or "Unlinked Client"
secondary = ""
context = ""
scope_label = "Client"
scope_type = (getattr(subscription, "scope_type", None) or "client").strip().lower()
if scope_type == "registration":
registration = registrations.get(getattr(subscription, "registration_id", None))
registration_type = registration_types.get(getattr(subscription, "registration_id", None))
if registration:
business = businesses.get(
getattr(registration, "business_unit_id", None)
or getattr(subscription, "business_unit_id", None)
)
branch = branches.get(
getattr(registration, "client_branch_id", None)
or getattr(subscription, "client_branch_id", None)
)
business_trade_name = (
(getattr(business, "trade_name", None) or "").strip()
if business else ""
)
business_name = (
(getattr(business, "business_name", None) or "").strip()
if business else ""
)
branch_name = (
(getattr(branch, "branch_name", None) or "").strip()
if branch else ""
)
primary = (
(getattr(registration, "trade_name", None) or "").strip()
or business_trade_name
or business_name
or branch_name
or (getattr(registration, "legal_name", None) or "").strip()
or client_trade_name
or client_name
or "Unlinked Client"
)
registration_code = (
(getattr(registration_type, "code", None) or "").strip()
if registration_type else ""
)
registration_number = (
getattr(registration, "registration_number", None) or ""
).strip()
if registration_code and registration_number:
secondary = f"{registration_code} · {registration_number}"
else:
secondary = registration_number or registration_code
if client_name and client_name != primary:
context = f"Client: {client_name}"
scope_label = "Registration"
elif scope_type == "business_unit":
business = businesses.get(getattr(subscription, "business_unit_id", None))
if business:
primary = (
(getattr(business, "trade_name", None) or "").strip()
or (getattr(business, "business_name", None) or "").strip()
or primary
)
secondary = (getattr(business, "business_code", None) or "").strip()
if client_name and client_name != primary:
context = f"Client: {client_name}"
scope_label = "Business Unit"
elif scope_type == "client_branch":
branch = branches.get(getattr(subscription, "client_branch_id", None))
business = businesses.get(
getattr(branch, "business_unit_id", None)
if branch else getattr(subscription, "business_unit_id", None)
)
if branch:
primary = (getattr(branch, "branch_name", None) or "").strip() or primary
secondary = (getattr(branch, "branch_code", None) or "").strip()
business_display = ""
if business:
business_display = (
(getattr(business, "trade_name", None) or "").strip()
or (getattr(business, "business_name", None) or "").strip()
)
context_parts: list[str] = []
if business_display and business_display != primary:
context_parts.append(business_display)
if client_name and client_name not in {primary, business_display}:
context_parts.append(f"Client: {client_name}")
context = " · ".join(context_parts)
scope_label = "Client Branch"
result[int(subscription.id)] = {
"primary": primary,
"secondary": secondary,
"context": context,
"scope_label": scope_label,
"client_code": client_code,
"client_name": client_name,
"client_trade_name": client_trade_name,
}
return result
def list_employee_work_kanban(
db: Session,
scope: EmployeeScope,
@@ -2905,7 +3140,7 @@ def list_employee_work_kanban(
ClientServiceTaskInstance.task_name.ilike(like),
ClientServiceTaskInstance.description.ilike(like),
ClientServiceTaskInstance.client.has(
or_(Client.client_name.ilike(like), Client.client_code.ilike(like))
or_(Client.client_name.ilike(like), Client.trade_name.ilike(like), Client.client_code.ilike(like))
),
ClientServiceTaskInstance.catalogue.has(
or_(
@@ -2926,6 +3161,18 @@ def list_employee_work_kanban(
)
).scalars().all()
work_subscriptions: dict[int, ClientServiceSubscription] = {}
for task in tasks:
subscription = getattr(task, "subscription", None)
if subscription is not None and getattr(subscription, "id", None):
work_subscriptions[int(subscription.id)] = subscription
scope_display_map = _employee_work_scope_display_map(
db,
tenant_id=scope.tenant_id,
subscriptions=list(work_subscriptions.values()),
)
columns = [
{"code": "pending", "label": "Pending", "cards": []},
{"code": "in_progress", "label": "In Progress", "cards": []},
@@ -2945,12 +3192,29 @@ def list_employee_work_kanban(
)
if engagement_id not in engagement_lookup:
client = getattr(task, "client", None)
scope_display = scope_display_map.get(
int(engagement_id),
{
"primary": (
(getattr(client, "trade_name", None) or "").strip()
or getattr(client, "client_name", None)
or "Unlinked Client"
),
"secondary": "",
"context": "",
"scope_label": "Client",
"client_code": getattr(client, "client_code", None) or "",
},
)
engagement_lookup[engagement_id] = {
"engagement_id": engagement_id,
"subscription": subscription,
"label": _subscription_label(subscription, task),
"client_name": getattr(client, "client_name", None) or "Unlinked Client",
"client_code": getattr(client, "client_code", None) or "",
"client_name": scope_display["primary"],
"client_code": scope_display.get("client_code", "") or getattr(client, "client_code", None) or "",
"scope_secondary": scope_display.get("secondary", ""),
"scope_context": scope_display.get("context", ""),
"scope_label": scope_display.get("scope_label", "Client"),
"service_name": (
getattr(getattr(subscription, "catalogue", None), "service_name", None)
or getattr(getattr(task, "catalogue", None), "service_name", None)
@@ -3635,7 +3899,7 @@ def list_visible_work_assignment_dashboard(
or_(
ClientServiceTaskInstance.task_name.ilike(like),
ClientServiceTaskInstance.description.ilike(like),
ClientServiceTaskInstance.client.has(or_(Client.client_name.ilike(like), Client.client_code.ilike(like))),
ClientServiceTaskInstance.client.has(or_(Client.client_name.ilike(like), Client.trade_name.ilike(like), Client.client_code.ilike(like))),
ClientServiceTaskInstance.catalogue.has(or_(ServiceCatalogue.service_name.ilike(like), ServiceCatalogue.service_code.ilike(like))),
)
)
@@ -4029,7 +4293,7 @@ def list_engagement_progress_dashboard(
or_(
ClientServiceTaskInstance.task_name.ilike(like),
ClientServiceTaskInstance.description.ilike(like),
ClientServiceTaskInstance.client.has(or_(Client.client_name.ilike(like), Client.client_code.ilike(like))),
ClientServiceTaskInstance.client.has(or_(Client.client_name.ilike(like), Client.trade_name.ilike(like), Client.client_code.ilike(like))),
ClientServiceTaskInstance.catalogue.has(or_(ServiceCatalogue.service_name.ilike(like), ServiceCatalogue.service_code.ilike(like))),
)
)