aee0f09db8
- Datafabrik: Dockerfile fix, agentorkestrering fungerar - Vision: Identify-modell, FAISS, OCR alla testade - API: Alla 7 integrationstester passerade - Upplösare: Entitetsupplösning verifierad
262 lines
9.1 KiB
Python
262 lines
9.1 KiB
Python
"""
|
|
HR Management Module
|
|
Handles employees, contractors, payroll, and time tracking
|
|
"""
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from sqlalchemy.orm import Session
|
|
from typing import List, Optional
|
|
from datetime import datetime
|
|
|
|
from app.database import get_db
|
|
from app.models import Employee, Contractor, EmployeeStatus, User
|
|
from app.core.security import get_current_user
|
|
|
|
router = APIRouter(prefix="/hr", tags=["hr"])
|
|
|
|
# ─── EMPLOYEES ────────────────────────────────────────────────────────────────
|
|
|
|
@router.get("/employees")
|
|
async def list_employees(
|
|
status: Optional[EmployeeStatus] = None,
|
|
department: Optional[str] = None,
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""List all employees with optional filtering."""
|
|
query = db.query(Employee)
|
|
if status:
|
|
query = query.filter(Employee.status == status)
|
|
if department:
|
|
query = query.filter(Employee.department.ilike(f"%{department}%"))
|
|
employees = query.offset(skip).limit(limit).all()
|
|
return {
|
|
"employees": [
|
|
{
|
|
"id": e.id,
|
|
"employee_id": e.employee_id,
|
|
"full_name": f"{e.first_name} {e.last_name}",
|
|
"email": e.email,
|
|
"department": e.department,
|
|
"position": e.position,
|
|
"status": e.status.value,
|
|
"start_date": e.start_date.isoformat() if e.start_date else None,
|
|
"salary": float(e.salary) if e.salary else None,
|
|
"salary_currency": e.salary_currency,
|
|
}
|
|
for e in employees
|
|
],
|
|
"total": query.count(),
|
|
}
|
|
|
|
@router.post("/employees")
|
|
async def create_employee(
|
|
employee_id: str,
|
|
first_name: str,
|
|
last_name: str,
|
|
email: str,
|
|
department: str,
|
|
position: str,
|
|
salary: float,
|
|
start_date: datetime,
|
|
phone: Optional[str] = None,
|
|
manager_id: Optional[int] = None,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""Create a new employee record."""
|
|
employee = Employee(
|
|
employee_id=employee_id,
|
|
first_name=first_name,
|
|
last_name=last_name,
|
|
email=email,
|
|
phone=phone,
|
|
department=department,
|
|
position=position,
|
|
salary=salary,
|
|
start_date=start_date,
|
|
manager_id=manager_id,
|
|
)
|
|
db.add(employee)
|
|
db.commit()
|
|
db.refresh(employee)
|
|
return {"status": "created", "employee_id": employee.id}
|
|
|
|
@router.get("/employees/{employee_id}")
|
|
async def get_employee(
|
|
employee_id: int,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""Get employee details."""
|
|
employee = db.query(Employee).filter(Employee.id == employee_id).first()
|
|
if not employee:
|
|
raise HTTPException(status_code=404, detail="Employee not found")
|
|
return {
|
|
"id": employee.id,
|
|
"employee_id": employee.employee_id,
|
|
"first_name": employee.first_name,
|
|
"last_name": employee.last_name,
|
|
"email": employee.email,
|
|
"phone": employee.phone,
|
|
"department": employee.department,
|
|
"position": employee.position,
|
|
"status": employee.status.value,
|
|
"start_date": employee.start_date.isoformat() if employee.start_date else None,
|
|
"end_date": employee.end_date.isoformat() if employee.end_date else None,
|
|
"salary": float(employee.salary) if employee.salary else None,
|
|
"salary_currency": employee.salary_currency,
|
|
"manager_id": employee.manager_id,
|
|
}
|
|
|
|
@router.put("/employees/{employee_id}")
|
|
async def update_employee(
|
|
employee_id: int,
|
|
department: Optional[str] = None,
|
|
position: Optional[str] = None,
|
|
salary: Optional[float] = None,
|
|
status: Optional[EmployeeStatus] = None,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""Update employee information."""
|
|
employee = db.query(Employee).filter(Employee.id == employee_id).first()
|
|
if not employee:
|
|
raise HTTPException(status_code=404, detail="Employee not found")
|
|
|
|
if department:
|
|
employee.department = department
|
|
if position:
|
|
employee.position = position
|
|
if salary:
|
|
employee.salary = salary
|
|
if status:
|
|
employee.status = status
|
|
if status == EmployeeStatus.TERMINATED:
|
|
employee.end_date = datetime.utcnow()
|
|
|
|
db.commit()
|
|
db.refresh(employee)
|
|
return {"status": "updated", "employee_id": employee.id}
|
|
|
|
# ─── CONTRACTORS ──────────────────────────────────────────────────────────────
|
|
|
|
@router.get("/contractors")
|
|
async def list_contractors(
|
|
status: Optional[str] = None,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""List all contractors."""
|
|
query = db.query(Contractor)
|
|
if status:
|
|
query = query.filter(Contractor.status == status)
|
|
contractors = query.all()
|
|
return {
|
|
"contractors": [
|
|
{
|
|
"id": c.id,
|
|
"contractor_id": c.contractor_id,
|
|
"company_name": c.company_name,
|
|
"contact_name": c.contact_name,
|
|
"email": c.email,
|
|
"services": c.services,
|
|
"hourly_rate": float(c.hourly_rate) if c.hourly_rate else None,
|
|
"currency": c.currency,
|
|
"contract_start": c.contract_start.isoformat() if c.contract_start else None,
|
|
"contract_end": c.contract_end.isoformat() if c.contract_end else None,
|
|
"status": c.status,
|
|
}
|
|
for c in contractors
|
|
],
|
|
"total": query.count(),
|
|
}
|
|
|
|
@router.post("/contractors")
|
|
async def create_contractor(
|
|
contractor_id: str,
|
|
company_name: str,
|
|
contact_name: str,
|
|
email: str,
|
|
services: List[str],
|
|
hourly_rate: float,
|
|
contract_start: datetime,
|
|
contract_end: Optional[datetime] = None,
|
|
phone: Optional[str] = None,
|
|
currency: str = "SEK",
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""Create a new contractor record."""
|
|
contractor = Contractor(
|
|
contractor_id=contractor_id,
|
|
company_name=company_name,
|
|
contact_name=contact_name,
|
|
email=email,
|
|
phone=phone,
|
|
services=services,
|
|
hourly_rate=hourly_rate,
|
|
currency=currency,
|
|
contract_start=contract_start,
|
|
contract_end=contract_end,
|
|
)
|
|
db.add(contractor)
|
|
db.commit()
|
|
db.refresh(contractor)
|
|
return {"status": "created", "contractor_id": contractor.id}
|
|
|
|
@router.get("/contractors/{contractor_id}")
|
|
async def get_contractor(
|
|
contractor_id: int,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""Get contractor details."""
|
|
contractor = db.query(Contractor).filter(Contractor.id == contractor_id).first()
|
|
if not contractor:
|
|
raise HTTPException(status_code=404, detail="Contractor not found")
|
|
return {
|
|
"id": contractor.id,
|
|
"contractor_id": contractor.contractor_id,
|
|
"company_name": contractor.company_name,
|
|
"contact_name": contractor.contact_name,
|
|
"email": contractor.email,
|
|
"phone": contractor.phone,
|
|
"services": contractor.services,
|
|
"hourly_rate": float(contractor.hourly_rate) if contractor.hourly_rate else None,
|
|
"currency": contractor.currency,
|
|
"contract_start": contractor.contract_start.isoformat() if contractor.contract_start else None,
|
|
"contract_end": contractor.contract_end.isoformat() if contractor.contract_end else None,
|
|
"status": contractor.status,
|
|
}
|
|
|
|
# ─── PAYROLL ──────────────────────────────────────────────────────────────────
|
|
|
|
@router.get("/payroll/summary")
|
|
async def get_payroll_summary(
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""Get payroll summary."""
|
|
employees = db.query(Employee).filter(Employee.status == EmployeeStatus.ACTIVE).all()
|
|
contractors = db.query(Contractor).filter(Contractor.status == "active").all()
|
|
|
|
total_salary = sum(float(e.salary) for e in employees if e.salary)
|
|
total_contractor_cost = sum(float(c.hourly_rate) * 160 for c in contractors if c.hourly_rate) # Assume 160h/month
|
|
|
|
return {
|
|
"period": datetime.utcnow().strftime("%Y-%m"),
|
|
"employees": {
|
|
"count": len(employees),
|
|
"total_monthly_salary": total_salary,
|
|
"currency": "SEK",
|
|
},
|
|
"contractors": {
|
|
"count": len(contractors),
|
|
"estimated_monthly_cost": total_contractor_cost,
|
|
"currency": "SEK",
|
|
},
|
|
"total_monthly_cost": total_salary + total_contractor_cost,
|
|
}
|