Synchronise approved AQMM across staff and review boards
This commit is contained in:
@@ -34,6 +34,7 @@ from app.modules.services.execution import (
|
||||
apply_task_checklist_response,
|
||||
recalculate_task_aqmm_status,
|
||||
submit_task_for_review,
|
||||
synchronise_approved_engagement_acceptance_tasks,
|
||||
)
|
||||
|
||||
EMPLOYEE_STATUS = ["active", "inactive", "relieved"]
|
||||
@@ -4077,6 +4078,31 @@ def get_employee_engagement_work_board(
|
||||
document, checklist, AQMM and lock controls remain the source of truth.
|
||||
"""
|
||||
today = date.today()
|
||||
|
||||
# Cross-view AQMM self-heal: an engagement that has already passed the
|
||||
# engagement-level AQMM acceptance must never continue to show the four
|
||||
# duplicated setup rows as pending in the Staff work board. Reconcile the
|
||||
# persisted execution rows before the board queries them. The synchroniser
|
||||
# is deliberately limited to untouched acceptance/team/independence/KYC
|
||||
# setup rows and does not modify normal execution history, evidence or rework.
|
||||
subscription_stmt = select(ClientServiceSubscription).where(
|
||||
ClientServiceSubscription.id == int(engagement_id),
|
||||
ClientServiceSubscription.tenant_id == int(scope.tenant_id),
|
||||
)
|
||||
if scope.branch_id is not None:
|
||||
subscription_stmt = subscription_stmt.where(
|
||||
ClientServiceSubscription.branch_id == int(scope.branch_id)
|
||||
)
|
||||
subscription_for_sync = db.execute(subscription_stmt).scalar_one_or_none()
|
||||
if subscription_for_sync is not None:
|
||||
repaired = synchronise_approved_engagement_acceptance_tasks(
|
||||
db,
|
||||
subscription=subscription_for_sync,
|
||||
actor_user_id=int(scope.own_user_id),
|
||||
)
|
||||
if repaired:
|
||||
db.commit()
|
||||
|
||||
assigned_stmt = _employee_work_task_query(
|
||||
db, scope, assigned_only=True, financial_year=financial_year
|
||||
).where(ClientServiceTaskInstance.subscription_id == engagement_id)
|
||||
|
||||
@@ -17,7 +17,7 @@ from app.modules.employees.service import (
|
||||
_engagement_team,
|
||||
_weighted_progress,
|
||||
)
|
||||
from app.modules.services.execution import CLOSED_TASK_STATUSES
|
||||
from app.modules.services.execution import CLOSED_TASK_STATUSES, synchronise_approved_engagement_acceptance_tasks
|
||||
from app.modules.services.models import ClientServiceSubscription, ClientServiceTaskInstance, ServiceCatalogue, ServiceTaskComment
|
||||
|
||||
MANAGER_ROLES = {"Manager", "Branch Manager", "System Admin", "Firm Admin", "Partner"}
|
||||
@@ -599,6 +599,19 @@ def get_manager_review_workspace(
|
||||
if not tasks:
|
||||
return None
|
||||
|
||||
subscription_for_sync = getattr(tasks[0], "subscription", None)
|
||||
if subscription_for_sync is not None:
|
||||
repaired_setup_rows = synchronise_approved_engagement_acceptance_tasks(
|
||||
db,
|
||||
subscription=subscription_for_sync,
|
||||
actor_user_id=int(current_user.id),
|
||||
)
|
||||
if repaired_setup_rows:
|
||||
db.commit()
|
||||
tasks = list(db.execute(
|
||||
stmt.order_by(ClientServiceTaskInstance.sequence_no.asc(), ClientServiceTaskInstance.id.asc())
|
||||
).scalars().all())
|
||||
|
||||
task_rows = [_manager_review_task_row(task) for task in tasks]
|
||||
reviewable = [row for row in task_rows if row["review_state"] in {"pending_review", "reviewed", "rework"}]
|
||||
pending = [row for row in reviewable if row["review_state"] == "pending_review"]
|
||||
|
||||
@@ -18,6 +18,7 @@ from app.modules.services.execution import (
|
||||
CLOSED_TASK_STATUSES,
|
||||
aqmm_task_summary_for_subscription,
|
||||
closure_readiness_for_subscription,
|
||||
synchronise_approved_engagement_acceptance_tasks,
|
||||
)
|
||||
from app.modules.services.models import (
|
||||
ClientServiceSubscription,
|
||||
@@ -571,6 +572,17 @@ def _partner_review_queue_rows(db: Session, request, current_user, *, limit: int
|
||||
ClientServiceSubscription.review_partner_user_id == current_user.id,
|
||||
))
|
||||
subscriptions = list(db.execute(stmt.order_by(ClientServiceSubscription.current_due_date.is_(None), ClientServiceSubscription.current_due_date.asc(), ClientServiceSubscription.id.desc()).limit(limit)).scalars().all())
|
||||
|
||||
repaired_setup_rows = 0
|
||||
for sub in subscriptions:
|
||||
repaired_setup_rows += synchronise_approved_engagement_acceptance_tasks(
|
||||
db,
|
||||
subscription=sub,
|
||||
actor_user_id=int(current_user.id),
|
||||
)
|
||||
if repaired_setup_rows:
|
||||
db.commit()
|
||||
|
||||
rows: list[dict[str, Any]] = []
|
||||
for sub in subscriptions:
|
||||
task_stmt = select(ClientServiceTaskInstance).where(
|
||||
@@ -638,6 +650,14 @@ def get_partner_review_workspace(db: Session, request, current_user, *, subscrip
|
||||
}:
|
||||
return None
|
||||
|
||||
repaired_setup_rows = synchronise_approved_engagement_acceptance_tasks(
|
||||
db,
|
||||
subscription=subscription,
|
||||
actor_user_id=int(current_user.id),
|
||||
)
|
||||
if repaired_setup_rows:
|
||||
db.commit()
|
||||
|
||||
task_stmt = (
|
||||
select(ClientServiceTaskInstance)
|
||||
.options(
|
||||
|
||||
@@ -18,7 +18,12 @@ from app.modules.core.rbac.deps import get_user_permissions, get_user_roles
|
||||
from app.modules.documents.models import EngagementDocument
|
||||
from app.modules.clients.models import Client, ClientBranch, ClientBusinessUnit
|
||||
from app.modules.registrations.models import ClientRegistration, RegistrationType
|
||||
from app.modules.services.execution import CLOSED_TASK_STATUSES, TASK_PRIORITIES, TASK_STATUSES
|
||||
from app.modules.services.execution import (
|
||||
CLOSED_TASK_STATUSES,
|
||||
TASK_PRIORITIES,
|
||||
TASK_STATUSES,
|
||||
synchronise_approved_engagement_acceptance_tasks,
|
||||
)
|
||||
from app.modules.services.engagement_resources import build_engagement_resource_context
|
||||
from app.modules.services.models import (
|
||||
ClientServiceSubscription,
|
||||
@@ -408,6 +413,21 @@ def build_partner_payload(
|
||||
)
|
||||
option_stmt = _subscription_scope_filter(option_stmt, tenant_id, branch_id, current_user, roles, financial_year=None)
|
||||
option_engagements = db.execute(option_stmt).scalars().all()
|
||||
|
||||
# Keep the Review Board in step with the engagement-level AQMM workflow.
|
||||
# Older generated task rows can retain their original AQMM gate flags even
|
||||
# after acceptance is approved; reconcile only those duplicated setup rows
|
||||
# before task buckets/counts are built.
|
||||
repaired_setup_rows = 0
|
||||
for subscription in option_engagements:
|
||||
repaired_setup_rows += synchronise_approved_engagement_acceptance_tasks(
|
||||
db,
|
||||
subscription=subscription,
|
||||
actor_user_id=int(current_user.id),
|
||||
)
|
||||
if repaired_setup_rows:
|
||||
db.commit()
|
||||
|
||||
financial_year_options = sorted({(row.financial_year or "").strip() for row in option_engagements if (row.financial_year or "").strip()}, reverse=True)
|
||||
period_options = sorted({(row.period_label or "").strip() for row in option_engagements if (row.period_label or "").strip()})
|
||||
service_map = {}
|
||||
@@ -697,6 +717,14 @@ def partner_engagement_detail(request: Request, engagement_id: int):
|
||||
if not engagement:
|
||||
return _redirect_denied()
|
||||
|
||||
repaired_setup_rows = synchronise_approved_engagement_acceptance_tasks(
|
||||
db,
|
||||
subscription=engagement,
|
||||
actor_user_id=int(current_user.id),
|
||||
)
|
||||
if repaired_setup_rows:
|
||||
db.commit()
|
||||
|
||||
task_stmt = select(ClientServiceTaskInstance).options(
|
||||
selectinload(ClientServiceTaskInstance.client),
|
||||
selectinload(ClientServiceTaskInstance.catalogue),
|
||||
|
||||
Reference in New Issue
Block a user