661 lines
40 KiB
Python
661 lines
40 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import date, datetime, time, timezone
|
|
|
|
from sqlalchemy import Boolean, Date, DateTime, Float, ForeignKey, Integer, String, Text, Time, UniqueConstraint
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
|
|
from app.core.db.common import CommonBase
|
|
|
|
|
|
class Employee(CommonBase):
|
|
"""Tenant and branch aware employee master.
|
|
|
|
This is the v2 Employee Core foundation migrated from the older HRMS module.
|
|
It intentionally keeps attendance, leave, payroll, documents and ESS out of
|
|
this table so those features can be added safely in later phases.
|
|
"""
|
|
|
|
__tablename__ = "employees"
|
|
__table_args__ = (
|
|
UniqueConstraint("tenant_id", "employee_code", name="uq_employees_tenant_code"),
|
|
UniqueConstraint("tenant_id", "user_id", name="uq_employees_tenant_user"),
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
branch_id: Mapped[int] = mapped_column(ForeignKey("branches.id"), nullable=False, index=True)
|
|
user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
|
|
|
|
employee_code: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
|
|
full_name: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
|
|
email: Mapped[str | None] = mapped_column(String(255), nullable=True, index=True)
|
|
mobile: Mapped[str | None] = mapped_column(String(20), nullable=True)
|
|
alternate_mobile: Mapped[str | None] = mapped_column(String(20), nullable=True)
|
|
|
|
date_of_joining: Mapped[date | None] = mapped_column(Date, nullable=True)
|
|
date_of_leaving: Mapped[date | None] = mapped_column(Date, nullable=True)
|
|
employment_type: Mapped[str] = mapped_column(String(50), nullable=False, default="full_time", index=True)
|
|
status: Mapped[str] = mapped_column(String(30), nullable=False, default="active", index=True)
|
|
is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, index=True)
|
|
|
|
department: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
|
|
designation: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
|
|
reporting_manager_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
|
|
|
|
pan: Mapped[str | None] = mapped_column(String(20), nullable=True, index=True)
|
|
uan: Mapped[str | None] = mapped_column(String(30), nullable=True)
|
|
esi_no: Mapped[str | None] = mapped_column(String(30), nullable=True)
|
|
pf_no: Mapped[str | None] = mapped_column(String(30), nullable=True)
|
|
aadhaar_last4: Mapped[str | None] = mapped_column(String(4), nullable=True)
|
|
|
|
bank_name: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
|
bank_account_no: Mapped[str | None] = mapped_column(String(40), nullable=True)
|
|
bank_ifsc: Mapped[str | None] = mapped_column(String(20), nullable=True)
|
|
|
|
address: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
emergency_contact_name: Mapped[str | None] = mapped_column(String(200), nullable=True)
|
|
emergency_contact_mobile: Mapped[str | None] = mapped_column(String(20), nullable=True)
|
|
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
|
|
created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
created_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False
|
|
)
|
|
updated_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
onupdate=lambda: datetime.now(timezone.utc),
|
|
nullable=False,
|
|
)
|
|
|
|
user = relationship("User", foreign_keys=[user_id])
|
|
reporting_manager = relationship("User", foreign_keys=[reporting_manager_user_id])
|
|
|
|
|
|
|
|
class EmployeeRegistrationRequest(CommonBase):
|
|
"""Employee self-registration / linkage request for ESS onboarding.
|
|
|
|
This table is deliberately separate from employees so a logged-in user can
|
|
request an employee profile without immediately creating an employee master.
|
|
Firm Admin / Partner / Branch Manager can review and approve it.
|
|
"""
|
|
|
|
__tablename__ = "employee_registration_requests"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
branch_id: Mapped[int] = mapped_column(ForeignKey("branches.id"), nullable=False, index=True)
|
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
|
|
requested_employee_code: Mapped[str | None] = mapped_column(String(50), nullable=True, index=True)
|
|
full_name: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
|
|
email: Mapped[str | None] = mapped_column(String(255), nullable=True, index=True)
|
|
mobile: Mapped[str | None] = mapped_column(String(20), nullable=True)
|
|
department: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
|
|
designation: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
|
|
date_of_joining: Mapped[date | None] = mapped_column(Date, nullable=True)
|
|
remarks: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
|
|
status: Mapped[str] = mapped_column(String(30), nullable=False, default="pending", index=True)
|
|
review_notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
reviewed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
reviewed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
created_employee_id: Mapped[int | None] = mapped_column(ForeignKey("employees.id", ondelete="SET NULL"), nullable=True, index=True)
|
|
|
|
created_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False
|
|
)
|
|
updated_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
onupdate=lambda: datetime.now(timezone.utc),
|
|
nullable=False,
|
|
)
|
|
|
|
user = relationship("User", foreign_keys=[user_id])
|
|
reviewed_by = relationship("User", foreign_keys=[reviewed_by_user_id])
|
|
created_employee = relationship("Employee", foreign_keys=[created_employee_id])
|
|
|
|
|
|
|
|
class EmployeeAttendance(CommonBase):
|
|
"""Daily attendance records for employee self-service and admin review.
|
|
|
|
One row is maintained per employee per attendance date. Phase 6C keeps the
|
|
model deliberately simple and tenant/branch-safe. Geo/IP validation can be
|
|
added later without changing the employee master.
|
|
"""
|
|
|
|
__tablename__ = "employee_attendance"
|
|
__table_args__ = (
|
|
UniqueConstraint("tenant_id", "employee_id", "attendance_date", name="uq_employee_attendance_employee_date"),
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
branch_id: Mapped[int] = mapped_column(ForeignKey("branches.id"), nullable=False, index=True)
|
|
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
|
|
|
|
attendance_date: Mapped[date] = mapped_column(Date, nullable=False, index=True)
|
|
punch_in_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
punch_out_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
punch_in_local_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=False), nullable=True)
|
|
punch_out_local_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=False), nullable=True)
|
|
branch_timezone: Mapped[str] = mapped_column(String(64), nullable=False, default="Asia/Kolkata")
|
|
scheduled_start_local: Mapped[time | None] = mapped_column(Time, nullable=True)
|
|
scheduled_end_local: Mapped[time | None] = mapped_column(Time, nullable=True)
|
|
late_by_minutes: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
attendance_rule_status: Mapped[str | None] = mapped_column(String(40), nullable=True, index=True)
|
|
is_weekly_off: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
|
work_duration_minutes: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
|
|
status: Mapped[str] = mapped_column(String(30), nullable=False, default="present", index=True)
|
|
approval_status: Mapped[str] = mapped_column(String(30), nullable=False, default="approved", index=True)
|
|
source: Mapped[str] = mapped_column(String(30), nullable=False, default="self_punch", index=True)
|
|
remarks: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
|
|
punch_in_latitude: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
punch_in_longitude: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
punch_in_accuracy_meters: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
punch_in_distance_meters: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
punch_in_ip: Mapped[str | None] = mapped_column(String(80), nullable=True)
|
|
punch_in_geo_status: Mapped[str | None] = mapped_column(String(40), nullable=True, index=True)
|
|
punch_in_ip_status: Mapped[str | None] = mapped_column(String(40), nullable=True, index=True)
|
|
|
|
punch_out_latitude: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
punch_out_longitude: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
punch_out_accuracy_meters: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
punch_out_distance_meters: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
punch_out_ip: Mapped[str | None] = mapped_column(String(80), nullable=True)
|
|
punch_out_geo_status: Mapped[str | None] = mapped_column(String(40), nullable=True, index=True)
|
|
punch_out_ip_status: Mapped[str | None] = mapped_column(String(40), nullable=True, index=True)
|
|
|
|
reviewed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
reviewed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
review_notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
|
|
created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
created_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False
|
|
)
|
|
updated_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
onupdate=lambda: datetime.now(timezone.utc),
|
|
nullable=False,
|
|
)
|
|
|
|
employee = relationship("Employee", foreign_keys=[employee_id])
|
|
user = relationship("User", foreign_keys=[user_id])
|
|
reviewed_by = relationship("User", foreign_keys=[reviewed_by_user_id])
|
|
|
|
|
|
|
|
class EmployeeLeaveType(CommonBase):
|
|
"""Tenant/branch aware leave type master for Phase 6D."""
|
|
|
|
__tablename__ = "employee_leave_types"
|
|
__table_args__ = (
|
|
UniqueConstraint("tenant_id", "branch_id", "code", name="uq_employee_leave_types_tenant_branch_code"),
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
branch_id: Mapped[int] = mapped_column(ForeignKey("branches.id"), nullable=False, index=True)
|
|
|
|
code: Mapped[str] = mapped_column(String(30), nullable=False, index=True)
|
|
name: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
annual_quota_days: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
carry_forward_allowed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
|
allow_negative_balance: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
|
requires_approval: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
|
|
is_paid: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
|
|
is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, index=True)
|
|
|
|
created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
created_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False
|
|
)
|
|
updated_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
onupdate=lambda: datetime.now(timezone.utc),
|
|
nullable=False,
|
|
)
|
|
|
|
|
|
class EmployeeLeaveBalance(CommonBase):
|
|
"""Leave balance per employee and leave type."""
|
|
|
|
__tablename__ = "employee_leave_balances"
|
|
__table_args__ = (
|
|
UniqueConstraint("tenant_id", "employee_id", "leave_type_id", name="uq_employee_leave_balances_employee_type"),
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
branch_id: Mapped[int] = mapped_column(ForeignKey("branches.id"), nullable=False, index=True)
|
|
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
leave_type_id: Mapped[int] = mapped_column(ForeignKey("employee_leave_types.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
|
|
opening_days: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
credited_days: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
availed_days: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
adjusted_days: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
balance_days: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
|
|
updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
updated_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
onupdate=lambda: datetime.now(timezone.utc),
|
|
nullable=False,
|
|
)
|
|
|
|
employee = relationship("Employee", foreign_keys=[employee_id])
|
|
leave_type = relationship("EmployeeLeaveType", foreign_keys=[leave_type_id])
|
|
|
|
|
|
class EmployeeLeaveRequest(CommonBase):
|
|
"""Employee leave request, review and approval workflow."""
|
|
|
|
__tablename__ = "employee_leave_requests"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
branch_id: Mapped[int] = mapped_column(ForeignKey("branches.id"), nullable=False, index=True)
|
|
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
|
|
leave_type_id: Mapped[int] = mapped_column(ForeignKey("employee_leave_types.id", ondelete="RESTRICT"), nullable=False, index=True)
|
|
|
|
from_date: Mapped[date] = mapped_column(Date, nullable=False, index=True)
|
|
to_date: Mapped[date] = mapped_column(Date, nullable=False, index=True)
|
|
days: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
|
|
reason: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
status: Mapped[str] = mapped_column(String(30), nullable=False, default="pending", index=True)
|
|
request_source: Mapped[str] = mapped_column(String(30), nullable=False, default="employee_portal", index=True)
|
|
|
|
reviewed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
reviewed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
review_notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
|
|
created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
created_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False
|
|
)
|
|
updated_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
onupdate=lambda: datetime.now(timezone.utc),
|
|
nullable=False,
|
|
)
|
|
|
|
employee = relationship("Employee", foreign_keys=[employee_id])
|
|
user = relationship("User", foreign_keys=[user_id])
|
|
leave_type = relationship("EmployeeLeaveType", foreign_keys=[leave_type_id])
|
|
reviewed_by = relationship("User", foreign_keys=[reviewed_by_user_id])
|
|
|
|
|
|
|
|
class EmployeeDocumentType(CommonBase):
|
|
"""Tenant/branch aware employee document type master for Phase 6E."""
|
|
|
|
__tablename__ = "employee_document_types"
|
|
__table_args__ = (
|
|
UniqueConstraint("tenant_id", "branch_id", "code", name="uq_employee_document_types_tenant_branch_code"),
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
branch_id: Mapped[int] = mapped_column(ForeignKey("branches.id"), nullable=False, index=True)
|
|
|
|
code: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
|
|
name: Mapped[str] = mapped_column(String(150), nullable=False)
|
|
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
is_mandatory: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
|
allow_employee_upload: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
|
|
requires_verification: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
|
|
is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, index=True)
|
|
|
|
created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
created_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False
|
|
)
|
|
updated_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
onupdate=lambda: datetime.now(timezone.utc),
|
|
nullable=False,
|
|
)
|
|
|
|
|
|
class EmployeeDocument(CommonBase):
|
|
"""Employee document metadata.
|
|
|
|
Files are stored on disk under data/uploads/employee_documents. The DB keeps
|
|
only controlled metadata and the relative file path so a later storage
|
|
backend such as Nextcloud/local office storage can be introduced safely.
|
|
"""
|
|
|
|
__tablename__ = "employee_documents"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
branch_id: Mapped[int] = mapped_column(ForeignKey("branches.id"), nullable=False, index=True)
|
|
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
document_type_id: Mapped[int | None] = mapped_column(ForeignKey("employee_document_types.id", ondelete="SET NULL"), nullable=True, index=True)
|
|
|
|
title: Mapped[str] = mapped_column(String(200), nullable=False, index=True)
|
|
document_no: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
|
|
issue_date: Mapped[date | None] = mapped_column(Date, nullable=True)
|
|
expiry_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
|
|
|
|
original_filename: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
stored_filename: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
storage_path: Mapped[str] = mapped_column(String(500), nullable=False)
|
|
content_type: Mapped[str | None] = mapped_column(String(150), nullable=True)
|
|
file_size_bytes: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
|
|
status: Mapped[str] = mapped_column(String(30), nullable=False, default="uploaded", index=True)
|
|
visibility: Mapped[str] = mapped_column(String(30), nullable=False, default="employee_and_hr", index=True)
|
|
remarks: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
|
|
verified_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
verified_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
verification_notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
|
|
uploaded_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
created_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False
|
|
)
|
|
updated_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
onupdate=lambda: datetime.now(timezone.utc),
|
|
nullable=False,
|
|
)
|
|
|
|
employee = relationship("Employee", foreign_keys=[employee_id])
|
|
document_type = relationship("EmployeeDocumentType", foreign_keys=[document_type_id])
|
|
verified_by = relationship("User", foreign_keys=[verified_by_user_id])
|
|
uploaded_by = relationship("User", foreign_keys=[uploaded_by_user_id])
|
|
|
|
|
|
|
|
class EmployeeOnboardingChecklistItem(CommonBase):
|
|
"""Reusable tenant/branch onboarding checklist master."""
|
|
|
|
__tablename__ = "employee_onboarding_checklist_items"
|
|
__table_args__ = (
|
|
UniqueConstraint("tenant_id", "branch_id", "code", name="uq_employee_onboarding_items_tenant_branch_code"),
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
branch_id: Mapped[int] = mapped_column(ForeignKey("branches.id"), nullable=False, index=True)
|
|
|
|
code: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
|
|
title: Mapped[str] = mapped_column(String(200), nullable=False)
|
|
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
stage: Mapped[str] = mapped_column(String(50), nullable=False, default="joining", index=True)
|
|
default_due_days: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
is_mandatory: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
|
|
is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, index=True)
|
|
|
|
created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
created_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False
|
|
)
|
|
updated_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
onupdate=lambda: datetime.now(timezone.utc),
|
|
nullable=False,
|
|
)
|
|
|
|
|
|
class EmployeeOnboardingTask(CommonBase):
|
|
"""Employee-specific onboarding checklist task."""
|
|
|
|
__tablename__ = "employee_onboarding_tasks"
|
|
__table_args__ = (
|
|
UniqueConstraint("tenant_id", "employee_id", "checklist_item_id", name="uq_employee_onboarding_task_employee_item"),
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
branch_id: Mapped[int] = mapped_column(ForeignKey("branches.id"), nullable=False, index=True)
|
|
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
checklist_item_id: Mapped[int | None] = mapped_column(ForeignKey("employee_onboarding_checklist_items.id", ondelete="SET NULL"), nullable=True, index=True)
|
|
|
|
title: Mapped[str] = mapped_column(String(200), nullable=False)
|
|
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
stage: Mapped[str] = mapped_column(String(50), nullable=False, default="joining", index=True)
|
|
due_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
|
|
status: Mapped[str] = mapped_column(String(30), nullable=False, default="pending", index=True)
|
|
assigned_to_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
|
|
|
|
completed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
completed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
review_notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
|
|
created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
created_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False
|
|
)
|
|
updated_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
onupdate=lambda: datetime.now(timezone.utc),
|
|
nullable=False,
|
|
)
|
|
|
|
employee = relationship("Employee", foreign_keys=[employee_id])
|
|
checklist_item = relationship("EmployeeOnboardingChecklistItem", foreign_keys=[checklist_item_id])
|
|
assigned_to = relationship("User", foreign_keys=[assigned_to_user_id])
|
|
completed_by = relationship("User", foreign_keys=[completed_by_user_id])
|
|
|
|
|
|
class EmployeeOffboardingRequest(CommonBase):
|
|
"""Employee resignation/relieving/offboarding workflow request."""
|
|
|
|
__tablename__ = "employee_offboarding_requests"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
branch_id: Mapped[int] = mapped_column(ForeignKey("branches.id"), nullable=False, index=True)
|
|
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
|
|
|
|
request_type: Mapped[str] = mapped_column(String(50), nullable=False, default="resignation", index=True)
|
|
requested_relieving_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
|
|
approved_relieving_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
|
|
reason: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
handover_notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
status: Mapped[str] = mapped_column(String(30), nullable=False, default="pending", index=True)
|
|
|
|
requested_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
reviewed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
reviewed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
review_notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
completed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
completed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
|
|
created_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False
|
|
)
|
|
updated_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
onupdate=lambda: datetime.now(timezone.utc),
|
|
nullable=False,
|
|
)
|
|
|
|
employee = relationship("Employee", foreign_keys=[employee_id])
|
|
requested_by = relationship("User", foreign_keys=[requested_by_user_id])
|
|
reviewed_by = relationship("User", foreign_keys=[reviewed_by_user_id])
|
|
completed_by = relationship("User", foreign_keys=[completed_by_user_id])
|
|
|
|
|
|
class EmployeeOffboardingTask(CommonBase):
|
|
"""Offboarding checklist task linked to an offboarding request."""
|
|
|
|
__tablename__ = "employee_offboarding_tasks"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
branch_id: Mapped[int] = mapped_column(ForeignKey("branches.id"), nullable=False, index=True)
|
|
request_id: Mapped[int] = mapped_column(ForeignKey("employee_offboarding_requests.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
|
|
title: Mapped[str] = mapped_column(String(200), nullable=False)
|
|
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
due_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
|
|
status: Mapped[str] = mapped_column(String(30), nullable=False, default="pending", index=True)
|
|
assigned_to_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
|
|
completed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
completed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
review_notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
|
|
created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
created_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False
|
|
)
|
|
updated_at_utc: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(timezone.utc),
|
|
onupdate=lambda: datetime.now(timezone.utc),
|
|
nullable=False,
|
|
)
|
|
|
|
request = relationship("EmployeeOffboardingRequest", foreign_keys=[request_id])
|
|
employee = relationship("Employee", foreign_keys=[employee_id])
|
|
assigned_to = relationship("User", foreign_keys=[assigned_to_user_id])
|
|
completed_by = relationship("User", foreign_keys=[completed_by_user_id])
|
|
|
|
|
|
|
|
class EmployeeSalaryStructure(CommonBase):
|
|
"""Employee salary structure header for Phase 6G payroll foundation."""
|
|
|
|
__tablename__ = "employee_salary_structures"
|
|
__table_args__ = (
|
|
UniqueConstraint("tenant_id", "employee_id", "effective_from", name="uq_employee_salary_structure_effective"),
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
branch_id: Mapped[int] = mapped_column(ForeignKey("branches.id"), nullable=False, index=True)
|
|
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
|
|
effective_from: Mapped[date] = mapped_column(Date, nullable=False, index=True)
|
|
effective_to: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
|
|
pay_cycle: Mapped[str] = mapped_column(String(30), nullable=False, default="monthly", index=True)
|
|
monthly_ctc_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
basic_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
hra_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
allowance_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
employee_pf_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
employee_esi_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
professional_tax_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
tds_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
other_deduction_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, index=True)
|
|
remarks: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
|
|
created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
created_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False)
|
|
updated_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False)
|
|
|
|
employee = relationship("Employee", foreign_keys=[employee_id])
|
|
|
|
|
|
class EmployeePayrollRun(CommonBase):
|
|
"""Monthly payroll run header."""
|
|
|
|
__tablename__ = "employee_payroll_runs"
|
|
__table_args__ = (
|
|
UniqueConstraint("tenant_id", "branch_id", "pay_year", "pay_month", name="uq_employee_payroll_run_period"),
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
branch_id: Mapped[int] = mapped_column(ForeignKey("branches.id"), nullable=False, index=True)
|
|
pay_year: Mapped[int] = mapped_column(Integer, nullable=False, index=True)
|
|
pay_month: Mapped[int] = mapped_column(Integer, nullable=False, index=True)
|
|
run_name: Mapped[str] = mapped_column(String(150), nullable=False)
|
|
status: Mapped[str] = mapped_column(String(30), nullable=False, default="draft", index=True)
|
|
total_employees: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
gross_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
deduction_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
net_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
|
|
processed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
approved_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
approved_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
paid_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
paid_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
created_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False)
|
|
updated_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False)
|
|
|
|
processed_by = relationship("User", foreign_keys=[processed_by_user_id])
|
|
approved_by = relationship("User", foreign_keys=[approved_by_user_id])
|
|
paid_by = relationship("User", foreign_keys=[paid_by_user_id])
|
|
|
|
|
|
class EmployeePayslip(CommonBase):
|
|
"""Employee payslip generated from an approved salary structure."""
|
|
|
|
__tablename__ = "employee_payslips"
|
|
__table_args__ = (
|
|
UniqueConstraint("tenant_id", "payroll_run_id", "employee_id", name="uq_employee_payslip_run_employee"),
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
branch_id: Mapped[int] = mapped_column(ForeignKey("branches.id"), nullable=False, index=True)
|
|
payroll_run_id: Mapped[int] = mapped_column(ForeignKey("employee_payroll_runs.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
employee_id: Mapped[int] = mapped_column(ForeignKey("employees.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
salary_structure_id: Mapped[int | None] = mapped_column(ForeignKey("employee_salary_structures.id", ondelete="SET NULL"), nullable=True, index=True)
|
|
|
|
pay_year: Mapped[int] = mapped_column(Integer, nullable=False, index=True)
|
|
pay_month: Mapped[int] = mapped_column(Integer, nullable=False, index=True)
|
|
basic_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
hra_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
allowance_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
gross_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
employee_pf_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
employee_esi_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
professional_tax_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
tds_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
other_deduction_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
deduction_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
net_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
status: Mapped[str] = mapped_column(String(30), nullable=False, default="generated", index=True)
|
|
remarks: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
|
|
generated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
|
created_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False)
|
|
updated_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False)
|
|
|
|
payroll_run = relationship("EmployeePayrollRun", foreign_keys=[payroll_run_id])
|
|
employee = relationship("Employee", foreign_keys=[employee_id])
|
|
salary_structure = relationship("EmployeeSalaryStructure", foreign_keys=[salary_structure_id])
|
|
generated_by = relationship("User", foreign_keys=[generated_by_user_id])
|