Files
njts-accounting-core/backend/app/payroll/employees/router.py
2026-01-20 11:15:49 +09:00

197 lines
7.7 KiB
Python

"""
従業員管理API (Employee Management API)
"""
from fastapi import APIRouter, HTTPException, status
from typing import List
from . import schemas
from ...core.database import get_connection
router = APIRouter(prefix="/payroll/employees", tags=["Payroll - Employees"])
@router.post("/", response_model=schemas.Employee, status_code=status.HTTP_201_CREATED)
def create_employee(employee: schemas.EmployeeCreate):
"""従業員を作成"""
with get_connection() as conn:
with conn.cursor() as cur:
cur.execute(
"""
INSERT INTO employees (
employee_code, name, name_kana, birth_date, gender,
email, phone, postal_code, address, hire_date, termination_date, is_active,
bank_name, bank_branch, bank_account_number, bank_account_type, notes
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
RETURNING *
""",
(
employee.employee_code, employee.name, employee.name_kana,
employee.birth_date, employee.gender,
employee.email, employee.phone, employee.postal_code, employee.address,
employee.hire_date, employee.termination_date, employee.is_active,
employee.bank_name, employee.bank_branch,
employee.bank_account_number, employee.bank_account_type, employee.notes
),
)
result = cur.fetchone()
conn.commit()
return result
@router.get("/", response_model=List[schemas.Employee])
def get_employees(is_active: bool = None):
"""従業員一覧を取得"""
with get_connection() as conn:
with conn.cursor() as cur:
if is_active is not None:
cur.execute(
"SELECT * FROM employees WHERE is_active = %s ORDER BY employee_code",
(is_active,)
)
else:
cur.execute("SELECT * FROM employees ORDER BY employee_code")
return cur.fetchall()
@router.get("/{employee_id}", response_model=schemas.EmployeeWithDependents)
def get_employee(employee_id: int):
"""従業員の詳細を取得(扶養家族を含む)"""
with get_connection() as conn:
with conn.cursor() as cur:
# 従業員情報取得
cur.execute("SELECT * FROM employees WHERE employee_id = %s", (employee_id,))
employee = cur.fetchone()
if not employee:
raise HTTPException(status_code=404, detail="従業員が見つかりません")
# 扶養家族情報取得
cur.execute(
"""
SELECT * FROM dependents
WHERE employee_id = %s
AND (valid_to IS NULL OR valid_to >= CURRENT_DATE)
ORDER BY valid_from
""",
(employee_id,)
)
dependents = cur.fetchall()
return {**employee, "dependents": dependents}
@router.put("/{employee_id}", response_model=schemas.Employee)
def update_employee(employee_id: int, employee: schemas.EmployeeUpdate):
"""従業員情報を更新"""
update_data = employee.model_dump(exclude_unset=True)
if not update_data:
raise HTTPException(status_code=400, detail="更新するデータがありません")
set_clause = ", ".join([f"{key} = %s" for key in update_data.keys()])
values = list(update_data.values()) + [employee_id]
with get_connection() as conn:
with conn.cursor() as cur:
cur.execute(
f"UPDATE employees SET {set_clause} WHERE employee_id = %s RETURNING *",
values,
)
result = cur.fetchone()
if not result:
raise HTTPException(status_code=404, detail="従業員が見つかりません")
conn.commit()
return result
@router.delete("/{employee_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_employee(employee_id: int):
"""従業員を削除"""
with get_connection() as conn:
with conn.cursor() as cur:
cur.execute("DELETE FROM employees WHERE employee_id = %s", (employee_id,))
if cur.rowcount == 0:
raise HTTPException(status_code=404, detail="従業員が見つかりません")
conn.commit()
# ========================================
# 扶養家族管理
# ========================================
@router.post("/{employee_id}/dependents", response_model=schemas.Dependent, status_code=status.HTTP_201_CREATED)
def create_dependent(employee_id: int, dependent: schemas.DependentCreate):
"""扶養家族を追加"""
with get_connection() as conn:
with conn.cursor() as cur:
# 従業員の存在確認
cur.execute("SELECT employee_id FROM employees WHERE employee_id = %s", (employee_id,))
if not cur.fetchone():
raise HTTPException(status_code=404, detail="従業員が見つかりません")
cur.execute(
"""
INSERT INTO dependents (
employee_id, name, relationship, birth_date,
is_spouse, is_disabled, income_amount, valid_from, valid_to
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
RETURNING *
""",
(
employee_id, dependent.name, dependent.relationship, dependent.birth_date,
dependent.is_spouse, dependent.is_disabled, dependent.income_amount,
dependent.valid_from, dependent.valid_to
),
)
result = cur.fetchone()
conn.commit()
return result
@router.get("/{employee_id}/dependents", response_model=List[schemas.Dependent])
def get_dependents(employee_id: int):
"""従業員の扶養家族一覧を取得"""
with get_connection() as conn:
with conn.cursor() as cur:
cur.execute(
"""
SELECT * FROM dependents
WHERE employee_id = %s
ORDER BY valid_from DESC
""",
(employee_id,)
)
return cur.fetchall()
@router.put("/dependents/{dependent_id}", response_model=schemas.Dependent)
def update_dependent(dependent_id: int, dependent: schemas.DependentUpdate):
"""扶養家族情報を更新"""
update_data = dependent.model_dump(exclude_unset=True)
if not update_data:
raise HTTPException(status_code=400, detail="更新するデータがありません")
set_clause = ", ".join([f"{key} = %s" for key in update_data.keys()])
values = list(update_data.values()) + [dependent_id]
with get_connection() as conn:
with conn.cursor() as cur:
cur.execute(
f"UPDATE dependents SET {set_clause} WHERE dependent_id = %s RETURNING *",
values,
)
result = cur.fetchone()
if not result:
raise HTTPException(status_code=404, detail="扶養家族が見つかりません")
conn.commit()
return result
@router.delete("/dependents/{dependent_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_dependent(dependent_id: int):
"""扶養家族を削除"""
with get_connection() as conn:
with conn.cursor() as cur:
cur.execute("DELETE FROM dependents WHERE dependent_id = %s", (dependent_id,))
if cur.rowcount == 0:
raise HTTPException(status_code=404, detail="扶養家族が見つかりません")
conn.commit()