From 436a0e0766b9f6303bb9935a6f7ce3006e02f826 Mon Sep 17 00:00:00 2001 From: A R R R Associates Date: Wed, 26 Aug 2026 17:27:32 +0530 Subject: [PATCH] Allow staff work assignments across financial years --- app/modules/employees/service.py | 26 +++++++++++++++- .../templates/employees/self_work.html | 14 ++++++--- app/modules/employees/ui.py | 31 ++++++++++--------- 3 files changed, 51 insertions(+), 20 deletions(-) diff --git a/app/modules/employees/service.py b/app/modules/employees/service.py index 78651c5..b5fcd1a 100644 --- a/app/modules/employees/service.py +++ b/app/modules/employees/service.py @@ -3158,6 +3158,28 @@ def list_employee_work_kanban( if page_size not in {10, 25, 50}: page_size = 10 + # Staff work is cross-FY by default. The global ERP FY must not silently + # hide an engagement assigned to this employee. financial_year is therefore + # an explicit optional filter only. + fy_stmt = ( + select(ClientServiceTaskInstance.financial_year) + .where( + ClientServiceTaskInstance.tenant_id == scope.tenant_id, + ClientServiceTaskInstance.is_active.is_(True), + _employee_owned_task_condition(scope), + ) + ) + if scope.branch_id is not None: + fy_stmt = fy_stmt.where(ClientServiceTaskInstance.branch_id == scope.branch_id) + financial_year_options = sorted( + { + str(value).strip() + for value in db.execute(fy_stmt).scalars().all() + if str(value or "").strip() + }, + reverse=True, + ) + stmt = _employee_work_task_query( db, scope, @@ -3309,7 +3331,7 @@ def list_employee_work_kanban( if not is_closed and not card["next_task_name"]: card["next_task_name"] = task.task_name - # Options are derived from the employee's assigned engagements in the active FY. + # Options are derived from the employee's assigned engagements in the selected FY, or all assigned years when no FY filter is selected. period_options = sorted({str(card.get("period_label") or "").strip() for card in engagement_lookup.values() if str(card.get("period_label") or "").strip()}) service_options = sorted({str(card.get("service_name") or "").strip() for card in engagement_lookup.values() if str(card.get("service_name") or "").strip()}, key=str.casefold) @@ -3493,6 +3515,7 @@ def list_employee_work_kanban( "status": status_filter, "today": today, "filters": { + "financial_year": (financial_year or "").strip(), "period": period_filter, "service": service_filter, "due": due_filter, @@ -3500,6 +3523,7 @@ def list_employee_work_kanban( "page_size": page_size, }, "filter_options": { + "financial_years": financial_year_options, "periods": period_options, "services": service_options, }, diff --git a/app/modules/employees/templates/employees/self_work.html b/app/modules/employees/templates/employees/self_work.html index 947f138..8d9e5e4 100644 --- a/app/modules/employees/templates/employees/self_work.html +++ b/app/modules/employees/templates/employees/self_work.html @@ -31,7 +31,7 @@
-
+
+
-
FY {{ financial_year }} · Default ordering remains earliest due date first.
+
{% if financial_year %}Filtered to FY {{ financial_year }}{% else %}All assigned financial years{% endif %} · Default ordering remains earliest due date first.
Clear @@ -141,14 +147,14 @@
{% if column.pagination.has_previous %} - + {% endif %} Page {{ column.pagination.page }} of {{ column.pagination.pages }} {% if column.pagination.has_next %}
- +
{% endif %} diff --git a/app/modules/employees/ui.py b/app/modules/employees/ui.py index a94d563..caf83c6 100644 --- a/app/modules/employees/ui.py +++ b/app/modules/employees/ui.py @@ -1934,7 +1934,7 @@ def employee_work_assignment_dashboard( selected_assigned_to_user_id=assignee_id, scope=scope, errors=[], - financial_year=financial_year, + financial_year=selected_financial_year or "", ) finally: db.close() @@ -2018,7 +2018,7 @@ def employee_my_work_task_status( status=status, remarks=remarks, actor_user_id=current_user.id, - financial_year=_active_financial_year(request), + financial_year=None, ) safe_return_url = return_url if return_url.startswith("/employee/work") else "/employee/work" return RedirectResponse(url=safe_return_url, status_code=303) @@ -2031,6 +2031,7 @@ def employee_my_work( request: Request, q: str = "", status: str = "open", + financial_year: str = "", period: str = "", service: str = "", due: str = "", @@ -2054,13 +2055,13 @@ def employee_my_work( branch_id = request.session.get("active_branch_id") scope = build_employee_scope(db, current_user, tenant_id=tenant_id, branch_id=branch_id) employee = get_employee_for_user(db, current_user) - financial_year = _active_financial_year(request) + selected_financial_year = (financial_year or "").strip() or None work_payload = list_employee_work_kanban( db, scope, q=q, status=status, - financial_year=financial_year, + financial_year=selected_financial_year, period=period, service=service, due_bucket=due, @@ -2125,7 +2126,7 @@ def employee_my_work_engagement_start( scope, engagement_id, actor_user_id=current_user.id, - financial_year=_active_financial_year(request), + financial_year=None, ) return RedirectResponse( url=f"/employee/work/engagements/{engagement_id}", @@ -2150,7 +2151,7 @@ def employee_my_work_engagement_board(request: Request, engagement_id: int, task branch_id = request.session.get("active_branch_id") scope = build_employee_scope(db, current_user, tenant_id=tenant_id, branch_id=branch_id) employee = get_employee_for_user(db, current_user) - financial_year = _active_financial_year(request) + financial_year = None board = get_employee_engagement_work_board( db, scope, @@ -2185,7 +2186,7 @@ def employee_workflow_pause(request: Request, engagement_id: int, reason: str = except PermissionError: return _csrf_rejected(request) scope = build_employee_scope(db, current_user, tenant_id=request.session.get("active_tenant_id") or current_user.tenant_id, branch_id=request.session.get("active_branch_id")) try: - pause_employee_engagement_workflow(db, scope, engagement_id, reason=reason, notes=notes, follow_up_date=follow_up_date, actor_user_id=current_user.id, financial_year=_active_financial_year(request)) + pause_employee_engagement_workflow(db, scope, engagement_id, reason=reason, notes=notes, follow_up_date=follow_up_date, actor_user_id=current_user.id, financial_year=None) except ValueError: db.rollback(); return RedirectResponse(url=f"/employee/work/engagements/{engagement_id}?pause_error=1", status_code=303) return RedirectResponse(url=f"/employee/work/engagements/{engagement_id}?paused=1", status_code=303) @@ -2204,7 +2205,7 @@ def employee_workflow_resume(request: Request, engagement_id: int, csrf_token: s except PermissionError: return _csrf_rejected(request) scope = build_employee_scope(db, current_user, tenant_id=request.session.get("active_tenant_id") or current_user.tenant_id, branch_id=request.session.get("active_branch_id")) try: - resume_employee_engagement_workflow(db, scope, engagement_id, actor_user_id=current_user.id, financial_year=_active_financial_year(request)) + resume_employee_engagement_workflow(db, scope, engagement_id, actor_user_id=current_user.id, financial_year=None) except ValueError: db.rollback(); return RedirectResponse(url=f"/employee/work/engagements/{engagement_id}?resume_error=1", status_code=303) return RedirectResponse(url=f"/employee/work/engagements/{engagement_id}?resumed=1", status_code=303) @@ -2234,7 +2235,7 @@ def employee_workflow_escalate( escalate_employee_engagement_workflow( db, scope, engagement_id, level=level, message=message, actor_user_id=current_user.id, - financial_year=request.session.get("active_financial_year"), + financial_year=None, ) except ValueError: db.rollback() @@ -2277,7 +2278,7 @@ def employee_workflow_task_quick_action( action=action, reason=reason, actor_user_id=current_user.id, - financial_year=_active_financial_year(request), + financial_year=None, ) except ValueError as exc: db.rollback() @@ -2341,7 +2342,7 @@ def employee_workflow_task_save( checklist_date_response=checklist_date_response, checklist_remarks=checklist_remarks, actor_user_id=current_user.id, - financial_year=_active_financial_year(request), + financial_year=None, ) except ValueError: db.rollback() @@ -2431,7 +2432,7 @@ def employee_work_task_add_communication( visibility=visibility, message=message, actor_user_id=current_user.id, - financial_year=_active_financial_year(request), + financial_year=None, ) return RedirectResponse(url=_safe_employee_return_url(return_to, f"/employees/work/tasks/{task_id}/communication"), status_code=303) finally: @@ -2455,7 +2456,7 @@ def employee_my_work_task_communication( tenant_id = request.session.get("active_tenant_id") or current_user.tenant_id branch_id = request.session.get("active_branch_id") scope = build_employee_scope(db, current_user, tenant_id=tenant_id, branch_id=branch_id) - financial_year = _active_financial_year(request) + financial_year = None task = get_work_task_with_communications(db, scope, task_id, assigned_only=True, financial_year=financial_year) return _render( request, @@ -2512,7 +2513,7 @@ def employee_my_work_task_add_communication( message=message, actor_user_id=current_user.id, assigned_only=True, - financial_year=_active_financial_year(request), + financial_year=None, ) return RedirectResponse(url=_safe_employee_return_url(return_to, f"/employee/work/tasks/{task_id}/communication"), status_code=303) finally: @@ -2870,7 +2871,7 @@ def _employee_portal_context(request: Request, db, current_user, *, active_tab: scope, q="", status="open", - financial_year=_active_financial_year(request), + financial_year=None, ) return { "title": "Employee Portal",