eafc1c33fc
- FastAPI backend with SQLite database - React + TailwindCSS frontend - JWT authentication - Excel upload/download for attendance and accident records - Shift roster management with S7a/S7b split - Lateness/OT calculation based on shift times - Roster info API endpoints - Export API with token query parameter support - Docker compose deployment
95 lines
3.8 KiB
Python
95 lines
3.8 KiB
Python
from sqlalchemy import Column, Integer, String, Text, Float, Boolean, DateTime, Date, Time, JSON, ForeignKey
|
|
from sqlalchemy.orm import relationship
|
|
from sqlalchemy.sql import func
|
|
from database import Base
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
def now_hkt():
|
|
"""Return current time in HKT timezone."""
|
|
return datetime.now(timezone.utc)
|
|
|
|
|
|
class User(Base):
|
|
__tablename__ = "users"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
email = Column(String(255), unique=True, index=True, nullable=False)
|
|
password_hash = Column(String(255), nullable=False)
|
|
name = Column(String(255), nullable=False)
|
|
role = Column(String(50), default="user") # admin, user
|
|
is_active = Column(Boolean, default=True)
|
|
created_at = Column(DateTime, default=now_hkt)
|
|
updated_at = Column(DateTime, default=now_hkt, onupdate=now_hkt)
|
|
deleted_at = Column(DateTime, nullable=True)
|
|
|
|
|
|
class Attendance(Base):
|
|
__tablename__ = "attendance"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
employee_id = Column(String(100), index=True, nullable=False)
|
|
employee_name = Column(String(255), nullable=False)
|
|
department = Column(String(100), index=True)
|
|
date = Column(Date, nullable=False)
|
|
check_in = Column(Time, nullable=True)
|
|
check_out = Column(Time, nullable=True)
|
|
overtime_hours = Column(Float, default=0)
|
|
leave_type = Column(String(50), nullable=True) # annual, sick, unpaid, other
|
|
leave_hours = Column(Float, default=0)
|
|
status = Column(String(50), nullable=False) # present, absent, late, leave
|
|
remarks = Column(Text, nullable=True)
|
|
custom_fields = Column(JSON, default={})
|
|
created_by = Column(Integer, ForeignKey("users.id"))
|
|
created_at = Column(DateTime, default=now_hkt)
|
|
updated_at = Column(DateTime, default=now_hkt, onupdate=now_hkt)
|
|
deleted_at = Column(DateTime, nullable=True) # Soft delete
|
|
|
|
|
|
class Accident(Base):
|
|
__tablename__ = "accidents"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
date = Column(Date, nullable=False)
|
|
time = Column(Time, nullable=True)
|
|
location = Column(String(255), nullable=False)
|
|
employee_name = Column(String(255), nullable=False)
|
|
employee_id = Column(String(100))
|
|
department = Column(String(100))
|
|
description = Column(Text, nullable=False)
|
|
severity = Column(String(50), nullable=False) # minor, moderate, serious, fatal
|
|
medical_report = Column(Text, nullable=True)
|
|
action_taken = Column(Text, nullable=True)
|
|
responsible_person = Column(String(255), nullable=True)
|
|
custom_fields = Column(JSON, default={})
|
|
created_by = Column(Integer, ForeignKey("users.id"))
|
|
created_at = Column(DateTime, default=now_hkt)
|
|
updated_at = Column(DateTime, default=now_hkt, onupdate=now_hkt)
|
|
deleted_at = Column(DateTime, nullable=True) # Soft delete
|
|
|
|
|
|
class AttendanceCustomField(Base):
|
|
__tablename__ = "attendance_custom_fields"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
field_name = Column(String(100), nullable=False)
|
|
field_type = Column(String(50), nullable=False) # text, number, date, select
|
|
field_key = Column(String(100), unique=True, nullable=False)
|
|
required = Column(Boolean, default=False)
|
|
options = Column(JSON, default=[]) # For select type
|
|
order = Column(Integer, default=0)
|
|
created_at = Column(DateTime, default=now_hkt)
|
|
|
|
|
|
class AccidentCustomField(Base):
|
|
__tablename__ = "accident_custom_fields"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
field_name = Column(String(100), nullable=False)
|
|
field_type = Column(String(50), nullable=False) # text, number, date, select
|
|
field_key = Column(String(100), unique=True, nullable=False)
|
|
required = Column(Boolean, default=False)
|
|
options = Column(JSON, default=[]) # For select type
|
|
order = Column(Integer, default=0)
|
|
created_at = Column(DateTime, default=now_hkt)
|