Files
boc/landvex-admin-backend/app/routers/hr.py
T
Bernt aee0f09db8 landvex: Fixar och tester klara för alla komponenter
- Datafabrik: Dockerfile fix, agentorkestrering fungerar
- Vision: Identify-modell, FAISS, OCR alla testade
- API: Alla 7 integrationstester passerade
- Upplösare: Entitetsupplösning verifierad
2026-07-05 06:41:32 +00:00

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,
}