From a40f9af975309ba94c2108735f497cc842316639 Mon Sep 17 00:00:00 2001 From: A R R R Associates Date: Fri, 31 Jul 2026 13:34:24 +0530 Subject: [PATCH] Add instant consultant client filtering without pagination --- app/modules/consultants/service.py | 72 ++----- .../consultants/portal_partials/clients.html | 185 +++++++++++------- 2 files changed, 130 insertions(+), 127 deletions(-) diff --git a/app/modules/consultants/service.py b/app/modules/consultants/service.py index 0efc127..9bed6f4 100644 --- a/app/modules/consultants/service.py +++ b/app/modules/consultants/service.py @@ -1,13 +1,11 @@ from __future__ import annotations -from math import ceil from datetime import date, datetime, timezone, timedelta import secrets from sqlalchemy import and_, func, or_, select from sqlalchemy.orm import Session, selectinload -from app.modules.clients.models import Client from app.modules.clients.models import Client, ClientAuditLog from app.modules.client_groups.models import ClientGroup from app.modules.consultants.models import ClientConsultantLink, ConsultantManagedClient, ConsultantProfile, ConsultantWorkspace, ConsultantServiceRequest @@ -338,20 +336,16 @@ def consultant_linked_clients_page( status: str = "active", page: int = 1, per_page: int = 25, - sort_by: str = "client_name", + sort_by: str = "client_code", sort_order: str = "asc", ) -> dict: - """Return a compact, searchable and paginated list of linked firm clients. + """Return every active consultant-linked firm client for instant browser filtering. - Link permission columns remain unchanged and continue to govern the existing - service, due-date, communication and document workflows. They are deliberately - not exposed in the client-list UI. + The existing link permission fields and all downstream service, due-date, + communication and document controls remain unchanged. Search, group, status + and sorting are performed instantly in the rendered client table, so no + pagination or form submission is required. """ - q = (q or "").strip() - status = (status or "").strip().lower() - per_page = min(max(int(per_page or 25), 1), 100) - page = max(int(page or 1), 1) - stmt = ( select(ClientConsultantLink, Client, ClientGroup) .join(Client, Client.id == ClientConsultantLink.client_id) @@ -363,53 +357,17 @@ def consultant_linked_clients_page( Client.tenant_id == consultant.tenant_id, Client.is_archived.is_(False), ) + .order_by(Client.client_code.asc(), Client.client_name.asc(), Client.id.asc()) ) - if status: - stmt = stmt.where(Client.status == status) - if client_group_id: - stmt = stmt.where(Client.client_group_id == int(client_group_id)) - if q: - like = f"%{q}%" - stmt = stmt.where( - or_( - Client.client_code.ilike(like), - Client.client_name.ilike(like), - Client.trade_name.ilike(like), - Client.pan.ilike(like), - Client.gstin.ilike(like), - ClientGroup.group_name.ilike(like), - ClientGroup.group_code.ilike(like), - ) - ) - - total = int(db.execute(select(func.count()).select_from(stmt.subquery())).scalar_one() or 0) - pages = max(ceil(total / per_page), 1) - page = min(page, pages) - - sort_columns = { - "client_code": Client.client_code, - "client_name": Client.client_name, - "pan": Client.pan, - "client_group": ClientGroup.group_name, - "status": Client.status, - } - sort_column = sort_columns.get(sort_by, Client.client_name) - ordering = sort_column.desc() if (sort_order or "asc").lower() == "desc" else sort_column.asc() rows = [] - result = db.execute( - stmt.order_by(ordering, Client.id.asc()) - .offset((page - 1) * per_page) - .limit(per_page) - ).all() - for link, client, group in result: + for link, client, group in db.execute(stmt).all(): rows.append({ "link": link, "client": client, "client_group_id": getattr(group, "id", None), "client_group_code": getattr(group, "group_code", None), "client_group_name": getattr(group, "group_name", None), - "relationship_label": (link.relationship_type or "accounts_consultant").replace("_", " ").title(), }) groups_stmt = ( @@ -431,16 +389,18 @@ def consultant_linked_clients_page( {"id": int(group_id), "code": code, "name": name} for group_id, code, name in db.execute(groups_stmt).all() ] + + total = len(rows) return { "rows": rows, "groups": groups, - "meta": {"total": total, "page": page, "per_page": per_page, "pages": pages}, + "meta": {"total": total, "page": 1, "per_page": total, "pages": 1}, "filters": { - "q": q, - "client_group_id": client_group_id, - "status": status, - "sort_by": sort_by if sort_by in sort_columns else "client_name", - "sort_order": "desc" if (sort_order or "asc").lower() == "desc" else "asc", + "q": "", + "client_group_id": None, + "status": "active", + "sort_by": "client_code", + "sort_order": "asc", }, } diff --git a/app/modules/consultants/templates/consultants/portal_partials/clients.html b/app/modules/consultants/templates/consultants/portal_partials/clients.html index 876fb3c..a2c7f06 100644 --- a/app/modules/consultants/templates/consultants/portal_partials/clients.html +++ b/app/modules/consultants/templates/consultants/portal_partials/clients.html @@ -1,74 +1,77 @@ {% set linked = payload.linked_clients_page %} -{% set filters = linked.filters %} {% set meta = linked.meta %}
-
+

Linked Firm Clients

Firm clients explicitly linked to your consultant profile.

- {{ meta.total }} client{% if meta.total != 1 %}s{% endif %} + + {{ meta.total }} client{% if meta.total != 1 %}s{% endif %} +
-
- +
-
- {% for group in linked.groups %} - + {% endfor %}
- + + +
- + + + + +
-
- - - - Clear -
- +
- + - - + - + {% for row in linked.rows %} - + {% set client_status = (row.client.status or 'inactive')|lower %} + - - {% else %} - {% endfor %} + + +
CodeClient NameClient Name PANClient GroupRoleClient Group Status
{{ row.client.client_code or '-' }}
{{ row.client.client_name }}
@@ -81,57 +84,21 @@ {% if row.client_group_code %}
{{ row.client_group_code }}
{% endif %} {% else %}-{% endif %}
{{ row.relationship_label }} - {% if row.client.status == 'active' %} + {% if client_status == 'active' %} Active {% else %} - {{ (row.client.status or 'inactive').replace('_',' ').title() }} + {{ client_status.replace('_',' ').title() }} {% endif %}
No linked firm clients match the selected filters.
- -
-
- {% if meta.total %} - Showing {{ ((meta.page - 1) * meta.per_page) + 1 }} to - {{ meta.total if meta.page * meta.per_page > meta.total else meta.page * meta.per_page }} - of {{ meta.total }} clients - {% else %}No clients found{% endif %} -
-
-
- - - {% if filters.client_group_id %}{% endif %} - - - - - - -
- {% set query = namespace(value='tab=clients&linked_q=' ~ (filters.q|urlencode) ~ '&linked_status=' ~ (filters.status|urlencode) ~ '&linked_per_page=' ~ meta.per_page ~ '&linked_sort_by=' ~ (filters.sort_by|urlencode) ~ '&linked_sort_order=' ~ (filters.sort_order|urlencode)) %} - {% if filters.client_group_id %}{% set query.value = query.value ~ '&linked_group_id=' ~ filters.client_group_id %}{% endif %} - {% if meta.page > 1 %}Previous{% endif %} - {% for number in range(1, meta.pages + 1) %} - {% if number == meta.page %} - {{ number }} - {% elif number == 1 or number == meta.pages or (number >= meta.page - 2 and number <= meta.page + 2) %} - {{ number }} - {% elif number == meta.page - 3 or number == meta.page + 3 %}{% endif %} - {% endfor %} - {% if meta.page < meta.pages %}Next{% endif %} -
-
@@ -155,3 +122,79 @@
+ +