Add client groups and family tracking

This commit is contained in:
A R R R Associates
2026-07-25 22:32:35 +05:30
parent 5e6584702c
commit 2dc2a712e8
21 changed files with 444 additions and 161 deletions
+28
View File
@@ -11,6 +11,7 @@ from sqlalchemy import select
from sqlalchemy.orm import Session
from app.modules.client_identity.service import ensure_identity, normalize_pan, placeholder_email
from app.modules.client_groups.service import get_group_by_code, resolve_or_create_group
from app.modules.clients import repository
from app.modules.clients.models import Client
from app.modules.clients.schemas import ClientCreate
@@ -31,6 +32,11 @@ TEMPLATE_COLUMNS = [
"referral_status",
"communication_routing_mode",
"branch_id",
"client_group_code",
"client_group_name",
"group_type",
"group_relationship",
"is_group_head",
"client_code",
"client_name",
"client_type",
@@ -75,6 +81,7 @@ TEMPLATE_COLUMNS = [
]
BOOL_FIELDS = {
"is_group_head",
"gst_applicable",
"income_tax_applicable",
"tds_applicable",
@@ -239,6 +246,11 @@ def build_client_import_template_bytes(*, current_user, tenant_id: int, partner_
"partner_user_id": "Must be an active Partner user in the same firm.",
"referred_by_consultant_id": "Optional active consultant profile id who introduced the client.",
"primary_consultant_id": "Optional active consultant profile id for the operational client link.",
"client_group_code": "Optional tenant-unique group code. Existing group is matched by this code.",
"client_group_name": "Required only when creating a new group during import.",
"group_type": "Family, Business Group, Promoter Group, Trust Group, Common Management, or Other.",
"group_relationship": "Optional relationship such as Spouse, HUF, Company, Trust, or Related Concern.",
"is_group_head": "yes/no. Only one group head is recommended per group.",
"referral_date": "Optional referral date supported by the existing client schema.",
"referral_reference": "Optional referral or source reference.",
"referral_status": "active, inactive, ended, or pending.",
@@ -407,6 +419,9 @@ def build_preview(
"tenant_id": firm_tenant_id,
"branch_id": branch_id,
"partner_id": partner_user_id or None,
"client_group_id": None,
"group_relationship": cleaned.get("group_relationship"),
"is_group_head": bool(cleaned.get("is_group_head")),
"referred_by_consultant_id": referred_id,
"primary_consultant_id": primary_id,
"referral_date": cleaned.get("referral_date"),
@@ -470,6 +485,9 @@ def build_preview(
"tenant_id": firm_tenant_id,
"branch_id": branch_id,
"partner_id": partner_user_id,
"client_group_code": cleaned.get("client_group_code"),
"client_group_name": cleaned.get("client_group_name"),
"group_type": cleaned.get("group_type") or "Family",
"client_payload": payload,
"portal_password": password,
"portal_password_confirm": password_confirm,
@@ -530,6 +548,16 @@ def commit_import(
if existing_user:
raise ValueError("Email is already used by another ERP login.")
group = resolve_or_create_group(
db, tenant_id=int(payload["tenant_id"]), actor_user_id=current_user.id,
group_code=item.get("client_group_code"), group_name=item.get("client_group_name"), group_type=item.get("group_type"),
)
payload["client_group_id"] = group.id if group else None
if group and payload.get("is_group_head"):
existing_head = db.execute(select(Client).where(Client.tenant_id == int(payload["tenant_id"]), Client.client_group_id == group.id, Client.is_group_head.is_(True))).scalar_one_or_none()
if existing_head:
raise ValueError(f"Group {group.group_code} already has group head {existing_head.client_code} - {existing_head.client_name}.")
data = ClientCreate(**payload)
row = create_client_service(
db,