From 0c5f555eb42779c3c9b0cfe4b0e4788d7bceec7b Mon Sep 17 00:00:00 2001 From: A R R R Associates Date: Wed, 2 Sep 2026 17:45:18 +0530 Subject: [PATCH] Show registration credentials in client business structure --- app/modules/clients/scope_ui.py | 53 ++++++++++++ .../templates/clients/business_structure.html | 81 ++++++++++++++++++- 2 files changed, 131 insertions(+), 3 deletions(-) diff --git a/app/modules/clients/scope_ui.py b/app/modules/clients/scope_ui.py index 5becb4c..260ebc7 100644 --- a/app/modules/clients/scope_ui.py +++ b/app/modules/clients/scope_ui.py @@ -12,10 +12,29 @@ from app.modules.clients.models import Client, ClientBusinessUnit, ClientBranch from app.modules.registrations.models import ClientRegistration, RegistrationType from app.modules.core.rbac.deps import get_user_permissions, get_user_roles from app.modules.core.rbac.permission_guard import require_permission +from app.modules.credential_vault.models import CredentialVaultEntry +from app.modules.credential_vault.service import can_manage_vault, can_open_vault, can_view_entry router = APIRouter(prefix="/clients", tags=["client-business-structure"]) +CREDENTIAL_CATEGORY_LABELS = { + "gst_portal": "GST Portal", + "income_tax_portal": "Income Tax Portal", + "traces_tds": "TRACES / TDS", + "mca_portal": "MCA Portal", + "eway_bill": "E-Way Bill", + "einvoice": "E-Invoice", + "government_portal": "Government / Registration Portal", + "banking": "Banking", + "email": "Email", + "software": "Software", + "api_key": "API / Provider", + "digital_signature": "Digital Signature / Token", + "other": "Other", +} + + def _tenant_id(request, user): return int(request.session.get("active_tenant_id") or request.session.get("tenant_id") or user.tenant_id) @@ -66,11 +85,45 @@ def business_structure_page(request: Request, client_id: int): RegistrationType.sort_order, RegistrationType.name ) ).scalars().all() + + # Credential Vault stays the single source of truth. This page only surfaces + # metadata/actions for credentials linked to the registration rows the user can + # already see; encrypted values are never decrypted here. + registration_credentials: dict[int, list[CredentialVaultEntry]] = {row.id: [] for row in registrations} + can_open_credentials = can_open_vault(db, user) + can_manage_credentials = can_manage_vault(db, user) + if registrations and can_open_credentials: + registration_ids = [int(row.id) for row in registrations] + credential_rows = db.execute( + select(CredentialVaultEntry).where( + CredentialVaultEntry.tenant_id == client.tenant_id, + CredentialVaultEntry.client_id == client.id, + CredentialVaultEntry.registration_id.in_(registration_ids), + ).order_by( + CredentialVaultEntry.status, + CredentialVaultEntry.category, + CredentialVaultEntry.title, + ) + ).scalars().all() + raw_active_branch_id = request.session.get("active_branch_id") + active_branch_id = ( + int(raw_active_branch_id) + if raw_active_branch_id not in (None, "", 0, "0") + else getattr(user, "branch_id", None) + ) + for credential in credential_rows: + if can_view_entry(db, user, credential, active_branch_id): + registration_credentials.setdefault(int(credential.registration_id), []).append(credential) + return templates.TemplateResponse( "modules/clients/templates/clients/business_structure.html", _context(request, db, user, title="Client Business Structure", client=client, businesses=businesses, branches=branches, registrations=registrations, registration_type_codes=registration_type_codes, registration_types=registration_types, + registration_credentials=registration_credentials, + credential_category_labels=CREDENTIAL_CATEGORY_LABELS, + can_open_credentials=can_open_credentials, + can_manage_credentials=can_manage_credentials, can_edit="clients.edit" in set(get_user_permissions(db, user.id))), ) finally: diff --git a/app/modules/clients/templates/clients/business_structure.html b/app/modules/clients/templates/clients/business_structure.html index 57a50e7..e54fae9 100644 --- a/app/modules/clients/templates/clients/business_structure.html +++ b/app/modules/clients/templates/clients/business_structure.html @@ -69,9 +69,84 @@
-
Registrations
- - {% for row in registrations %}{% else %}{% endfor %}
TypeNumberTrade / Unit NameStateStatus
{{ registration_type_codes.get(row.id, "-") }}{{ row.registration_number }}{{ row.trade_name or row.legal_name or '-' }}{{ row.state or '-' }}{{ row.status|replace('_',' ')|title }}{% if can_edit %}
{% endif %}
No registrations added.
+
+
+
Registrations
+
Portal credentials linked to each registration are available here without exposing encrypted values.
+
+
+
+ + + + + + + + + + + + + + {% for row in registrations %} + {% set linked_credentials = registration_credentials.get(row.id, []) %} + + + + + + + + + + {% else %} + + {% endfor %} + +
TypeNumberTrade / Unit NameStateStatusCredentialsActions
{{ registration_type_codes.get(row.id, "-") }}{{ row.registration_number }}{{ row.trade_name or row.legal_name or '-' }}{{ row.state or '-' }}{{ row.status|replace('_',' ')|title }} + {% if can_open_credentials %} + {% if linked_credentials %} +
+ + {{ linked_credentials|length }} credential{{ '' if linked_credentials|length == 1 else 's' }} + ▼ + +
+ {% for credential in linked_credentials %} +
+
+
+
{{ credential.title }}
+
{{ credential_category_labels.get(credential.category, credential.category|replace('_',' ')|title) }}
+
+ {{ credential.status|replace('_',' ')|title }} +
+ View credential +
+ {% endfor %} +
+
+ {% else %} + No credentials added + {% endif %} + {% else %} + Restricted + {% endif %} +
+
+ {% if can_manage_credentials %} + {{ '+ Add Another Credential' if linked_credentials else '+ Add Credential' }} + {% endif %} + {% if can_edit %} +
+ + +
+ {% endif %} +
+
No registrations added.
+