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,
|
apply_task_checklist_response,
|
||||||
recalculate_task_aqmm_status,
|
recalculate_task_aqmm_status,
|
||||||
submit_task_for_review,
|
submit_task_for_review,
|
||||||
|
synchronise_approved_engagement_acceptance_tasks,
|
||||||
)
|
)
|
||||||
|
|
||||||
EMPLOYEE_STATUS = ["active", "inactive", "relieved"]
|
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.
|
document, checklist, AQMM and lock controls remain the source of truth.
|
||||||
"""
|
"""
|
||||||
today = date.today()
|
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(
|
assigned_stmt = _employee_work_task_query(
|
||||||
db, scope, assigned_only=True, financial_year=financial_year
|
db, scope, assigned_only=True, financial_year=financial_year
|
||||||
).where(ClientServiceTaskInstance.subscription_id == engagement_id)
|
).where(ClientServiceTaskInstance.subscription_id == engagement_id)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from app.modules.employees.service import (
|
|||||||
_engagement_team,
|
_engagement_team,
|
||||||
_weighted_progress,
|
_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
|
from app.modules.services.models import ClientServiceSubscription, ClientServiceTaskInstance, ServiceCatalogue, ServiceTaskComment
|
||||||
|
|
||||||
MANAGER_ROLES = {"Manager", "Branch Manager", "System Admin", "Firm Admin", "Partner"}
|
MANAGER_ROLES = {"Manager", "Branch Manager", "System Admin", "Firm Admin", "Partner"}
|
||||||
@@ -599,6 +599,19 @@ def get_manager_review_workspace(
|
|||||||
if not tasks:
|
if not tasks:
|
||||||
return None
|
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]
|
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"}]
|
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"]
|
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,
|
CLOSED_TASK_STATUSES,
|
||||||
aqmm_task_summary_for_subscription,
|
aqmm_task_summary_for_subscription,
|
||||||
closure_readiness_for_subscription,
|
closure_readiness_for_subscription,
|
||||||
|
synchronise_approved_engagement_acceptance_tasks,
|
||||||
)
|
)
|
||||||
from app.modules.services.models import (
|
from app.modules.services.models import (
|
||||||
ClientServiceSubscription,
|
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,
|
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())
|
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]] = []
|
rows: list[dict[str, Any]] = []
|
||||||
for sub in subscriptions:
|
for sub in subscriptions:
|
||||||
task_stmt = select(ClientServiceTaskInstance).where(
|
task_stmt = select(ClientServiceTaskInstance).where(
|
||||||
@@ -638,6 +650,14 @@ def get_partner_review_workspace(db: Session, request, current_user, *, subscrip
|
|||||||
}:
|
}:
|
||||||
return None
|
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 = (
|
task_stmt = (
|
||||||
select(ClientServiceTaskInstance)
|
select(ClientServiceTaskInstance)
|
||||||
.options(
|
.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.documents.models import EngagementDocument
|
||||||
from app.modules.clients.models import Client, ClientBranch, ClientBusinessUnit
|
from app.modules.clients.models import Client, ClientBranch, ClientBusinessUnit
|
||||||
from app.modules.registrations.models import ClientRegistration, RegistrationType
|
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.engagement_resources import build_engagement_resource_context
|
||||||
from app.modules.services.models import (
|
from app.modules.services.models import (
|
||||||
ClientServiceSubscription,
|
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_stmt = _subscription_scope_filter(option_stmt, tenant_id, branch_id, current_user, roles, financial_year=None)
|
||||||
option_engagements = db.execute(option_stmt).scalars().all()
|
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)
|
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()})
|
period_options = sorted({(row.period_label or "").strip() for row in option_engagements if (row.period_label or "").strip()})
|
||||||
service_map = {}
|
service_map = {}
|
||||||
@@ -697,6 +717,14 @@ def partner_engagement_detail(request: Request, engagement_id: int):
|
|||||||
if not engagement:
|
if not engagement:
|
||||||
return _redirect_denied()
|
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(
|
task_stmt = select(ClientServiceTaskInstance).options(
|
||||||
selectinload(ClientServiceTaskInstance.client),
|
selectinload(ClientServiceTaskInstance.client),
|
||||||
selectinload(ClientServiceTaskInstance.catalogue),
|
selectinload(ClientServiceTaskInstance.catalogue),
|
||||||
|
|||||||
Reference in New Issue
Block a user