Add AQMM task evidence and review notes workflow

This commit is contained in:
A R R R Associates
2026-07-07 12:03:07 +05:30
parent 05690a220c
commit 91b1811eb8
10 changed files with 816 additions and 11 deletions
+123 -2
View File
@@ -18,6 +18,7 @@ from app.modules.services.execution import (
TASK_STATUSES,
add_task_comment,
apply_bulk_task_update,
apply_task_review,
apply_task_update,
dashboard_stats,
generate_tasks_for_subscription,
@@ -29,6 +30,8 @@ from app.modules.services.execution import (
list_subscription_execution_payload,
list_tasks_payload,
parse_date_value,
recalculate_task_aqmm_status,
submit_task_for_review,
)
router = APIRouter(prefix="/services/work-tracker", tags=["services-work-tracker-ui"])
@@ -119,10 +122,10 @@ def _can_update_task_status(db, user, task) -> bool:
def _allowed_comment_type_codes(db, user) -> set[str]:
if _can_bulk_manage_tasks(db, user):
return {"internal_note", "client_clarification", "consultant_clarification", "partner_review_note"}
return {"internal_note", "client_clarification", "consultant_clarification", "manager_review_note", "partner_review_note", "review_partner_review_note", "rework_note"}
if _is_staff(db, user):
# Staff may communicate through consultants for clients where consultant is the communication channel.
return {"internal_note", "client_clarification", "consultant_clarification"}
return {"internal_note", "staff_work_note", "client_clarification", "consultant_clarification"}
return set()
@@ -132,6 +135,32 @@ def _can_add_task_comment(db, user, task) -> bool:
return _can_update_task_status(db, user, task)
def _is_manager_role(db, user) -> bool:
roles = _role_names(db, user)
return bool({"Manager", "Branch Manager", "Firm Admin"}.intersection(roles))
def _can_submit_task_for_review(db, user, task) -> bool:
return _can_update_task_status(db, user, task)
def _can_manager_review_task(db, user, task) -> bool:
return _can_bulk_manage_tasks(db, user) or (_is_manager_role(db, user) and _has_perm(db, user, "service_tasks.edit"))
def _can_partner_review_task(db, user, task) -> bool:
return _can_bulk_manage_tasks(db, user)
def _can_review_partner_review_task(db, user, task) -> bool:
subscription = getattr(task, "subscription", None)
if not subscription:
return False
if _is_firm_admin(db, user):
return _has_perm(db, user, "service_tasks.edit")
return _is_partner(db, user) and int(getattr(subscription, "review_partner_user_id", 0) or 0) == int(user.id)
def _comment_type_options_for_user(db, user):
allowed = _allowed_comment_type_codes(db, user)
return [(code, label) for code, label in TASK_COMMENT_TYPES if code in allowed]
@@ -406,6 +435,10 @@ def task_edit_page(request: Request, task_id: int):
comment_type_options=_comment_type_options_for_user(db, user),
can_add_comment=_can_add_task_comment(db, user, task),
can_edit=_can_update_task_status(db, user, task),
can_submit_review=_can_submit_task_for_review(db, user, task),
can_manager_review=_can_manager_review_task(db, user, task),
can_partner_review=_can_partner_review_task(db, user, task),
can_review_partner_review=_can_review_partner_review_task(db, user, task),
can_manage_fields=_can_bulk_manage_tasks(db, user),
can_reassign=_can_assign_staff(db, user),
)
@@ -481,12 +514,100 @@ def task_edit_submit(
is_active=resolved_is_active,
user_id=user.id,
)
recalculate_task_aqmm_status(db, task)
db.commit()
return RedirectResponse(url="/services/work-tracker", status_code=303)
finally:
db.close()
@router.post("/tasks/{task_id}/submit-review")
def task_submit_review(
request: Request,
task_id: int,
note: str = Form(""),
csrf_token: str = Form(...),
):
validate_csrf(request, csrf_token)
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)
if not user:
return RedirectResponse(url="/login", status_code=303)
tenant_id = _active_tenant_id(request, user)
branch_id = _active_branch_id(request, user, db)
task = get_task(
db,
tenant_id=tenant_id,
task_id=task_id,
branch_id=branch_id,
assigned_to_user_id=_assigned_user_filter(db, user),
partner_user_id=_partner_visibility_user_id(db, user),
financial_year=_active_financial_year(request),
)
if not task:
return RedirectResponse(url="/services/work-tracker", status_code=303)
if not _can_submit_task_for_review(db, user, task):
return _redirect_denied()
try:
submit_task_for_review(db, task=task, note=note, user_id=user.id)
db.commit()
return RedirectResponse(url=f"/services/work-tracker/tasks/{task_id}/edit?submitted=1", status_code=303)
except ValueError:
db.rollback()
return RedirectResponse(url=f"/services/work-tracker/tasks/{task_id}/edit?review_error=1", status_code=303)
finally:
db.close()
@router.post("/tasks/{task_id}/review/{review_level}")
def task_review_submit(
request: Request,
task_id: int,
review_level: str,
decision: str = Form("reviewed"),
note: str = Form(""),
csrf_token: str = Form(...),
):
validate_csrf(request, csrf_token)
db = CommonSessionLocal()
try:
user = get_current_user(request, db=db)
if not user:
return RedirectResponse(url="/login", status_code=303)
tenant_id = _active_tenant_id(request, user)
branch_id = _active_branch_id(request, user, db)
task = get_task(
db,
tenant_id=tenant_id,
task_id=task_id,
branch_id=branch_id,
assigned_to_user_id=None,
partner_user_id=_partner_visibility_user_id(db, user),
financial_year=_active_financial_year(request),
)
if not task:
return RedirectResponse(url="/services/work-tracker", status_code=303)
allowed = False
if review_level == "manager":
allowed = _can_manager_review_task(db, user, task)
elif review_level == "partner":
allowed = _can_partner_review_task(db, user, task)
elif review_level == "review_partner":
allowed = _can_review_partner_review_task(db, user, task)
if not allowed:
return _redirect_denied()
try:
apply_task_review(db, task=task, review_level=review_level, decision=decision, note=note, user_id=user.id)
db.commit()
return RedirectResponse(url=f"/services/work-tracker/tasks/{task_id}/edit?reviewed=1", status_code=303)
except ValueError:
db.rollback()
return RedirectResponse(url=f"/services/work-tracker/tasks/{task_id}/edit?review_error=1", status_code=303)
finally:
db.close()
@router.post("/tasks/{task_id}/comments")
def task_comment_submit(
request: Request,