diff --git a/app/modules/firm_admin_dashboard/service.py b/app/modules/firm_admin_dashboard/service.py
index 6e7d802..5d07d87 100644
--- a/app/modules/firm_admin_dashboard/service.py
+++ b/app/modules/firm_admin_dashboard/service.py
@@ -18,6 +18,7 @@ from app.modules.core.rbac.models import Role, UserRole
from app.modules.core.tenancy.models import Branch, FinancialYear, Tenant
from app.modules.clients.models import Client
from app.modules.employees.models import Employee
+from app.modules.consultants.models import ConsultantProfile
from app.modules.services.models import (
FirmServiceSelection,
FirmServiceTaskTemplate,
@@ -268,6 +269,7 @@ ONBOARDING_ROLE_CONFIG: dict[str, dict[str, str]] = {
"default_designation": "Partner",
"default_department": "Management",
"default_employment_type": "full_time",
+ "requires_employee": True,
},
"manager": {
"role_name": "Branch Manager",
@@ -275,6 +277,7 @@ ONBOARDING_ROLE_CONFIG: dict[str, dict[str, str]] = {
"default_designation": "Branch Manager",
"default_department": "Operations",
"default_employment_type": "full_time",
+ "requires_employee": True,
},
"staff": {
"role_name": "Staff",
@@ -282,6 +285,15 @@ ONBOARDING_ROLE_CONFIG: dict[str, dict[str, str]] = {
"default_designation": "Staff",
"default_department": "Operations",
"default_employment_type": "full_time",
+ "requires_employee": True,
+ },
+ "consultant": {
+ "role_name": "Consultant",
+ "label": "Consultant",
+ "default_designation": "Consultant",
+ "default_department": "Consulting",
+ "default_employment_type": "consultant",
+ "requires_employee": False,
},
}
@@ -447,6 +459,13 @@ def create_firm_internal_user(
designation: str | None = None,
date_of_joining: str | None = None,
employment_type: str | None = None,
+ consultant_type: str | None = None,
+ firm_name: str | None = None,
+ specialisation: str | None = None,
+ gstin: str | None = None,
+ pan: str | None = None,
+ address: str | None = None,
+ remarks: str | None = None,
) -> dict[str, Any]:
actor_roles = set(get_user_role_names(db, actor.id))
if not actor_roles.intersection(FIRM_ADMIN_ROLES):
@@ -501,17 +520,60 @@ def create_firm_internal_user(
db.flush()
db.add(UserRole(user_id=user.id, role_id=role.id))
- employee_link_result = _ensure_employee_for_onboarded_user(
- db,
- actor=actor,
- user_obj=user,
- employee_code=employee_code,
- mobile=mobile,
- department=(department or config.get("default_department") or ""),
- designation=(designation or config.get("default_designation") or ""),
- date_of_joining=date_of_joining,
- employment_type=(employment_type or config.get("default_employment_type") or "full_time"),
- )
+ consultant_profile = None
+ if role_key == "consultant":
+ duplicate_profile = db.execute(
+ select(ConsultantProfile).where(
+ ConsultantProfile.tenant_id == int(tenant_id),
+ ConsultantProfile.email == email_clean,
+ )
+ ).scalar_one_or_none()
+ if duplicate_profile:
+ raise ValueError("A consultant profile with this email already exists in this firm.")
+
+ consultant_profile = ConsultantProfile(
+ tenant_id=int(tenant_id),
+ branch_id=int(branch.id),
+ user_id=int(user.id),
+ consultant_type=(consultant_type or "external_consultant").strip() or "external_consultant",
+ firm_name=(firm_name or "").strip() or None,
+ contact_person=name_clean,
+ email=email_clean,
+ mobile=(mobile or "").strip() or None,
+ specialisation=(specialisation or "").strip() or None,
+ gstin=(gstin or "").strip().upper() or None,
+ pan=(pan or "").strip().upper() or None,
+ address=(address or "").strip() or None,
+ status="active",
+ onboarding_status="invited",
+ is_platform_partner=False,
+ is_franchise_partner=False,
+ is_saas_customer=False,
+ is_active=True,
+ remarks=(remarks or "").strip() or None,
+ created_by_user_id=actor.id,
+ updated_by_user_id=actor.id,
+ )
+ db.add(consultant_profile)
+ db.flush()
+ employee_link_result = {
+ "required": False,
+ "created": False,
+ "linked_existing": False,
+ "employee_id": None,
+ }
+ else:
+ employee_link_result = _ensure_employee_for_onboarded_user(
+ db,
+ actor=actor,
+ user_obj=user,
+ employee_code=employee_code,
+ mobile=mobile,
+ department=(department or config.get("default_department") or ""),
+ designation=(designation or config.get("default_designation") or ""),
+ date_of_joining=date_of_joining,
+ employment_type=(employment_type or config.get("default_employment_type") or "full_time"),
+ )
try:
db.commit()
@@ -540,6 +602,7 @@ def create_firm_internal_user(
"role_label": config["label"],
"branch": branch,
"employee_link_result": employee_link_result,
+ "consultant_profile": consultant_profile,
"invite_url": invite_url,
"email_status": email_status,
"email_error": email_error,
diff --git a/app/modules/firm_admin_dashboard/templates/firm_admin_dashboard/partials/users.html b/app/modules/firm_admin_dashboard/templates/firm_admin_dashboard/partials/users.html
index b835f83..66872e6 100644
--- a/app/modules/firm_admin_dashboard/templates/firm_admin_dashboard/partials/users.html
+++ b/app/modules/firm_admin_dashboard/templates/firm_admin_dashboard/partials/users.html
@@ -8,6 +8,7 @@
+ Add Partner
+ Add Manager
+ Add Staff
+ + Add Consultant
Import Users
Template
Manage Users
diff --git a/app/modules/firm_admin_dashboard/templates/firm_admin_dashboard/partials/wizards.html b/app/modules/firm_admin_dashboard/templates/firm_admin_dashboard/partials/wizards.html
index 6d3f32b..d3a186b 100644
--- a/app/modules/firm_admin_dashboard/templates/firm_admin_dashboard/partials/wizards.html
+++ b/app/modules/firm_admin_dashboard/templates/firm_admin_dashboard/partials/wizards.html
@@ -5,6 +5,7 @@
Create partner user, link employee master and generate invite. Creates user with Branch Manager role. Create staff user, employee master and invite link. Create consultant login, consultant profile and invite link without creating an employee record. {{ wizard.desc }}
Firm Admin User Onboarding
Create the user account, assign branch and role, create/link employee master, generate invite link and attempt invite email.
+Create the user account, assign branch and role, create the appropriate profile, generate invite link and attempt invite email.
User Created
The user account, role mapping, branch assignment and employee master link have been completed.
+The user account, role mapping and branch assignment have been completed. The appropriate employee or consultant profile has also been created.