給与管理システム新規作成
This commit is contained in:
3
backend/app/payroll/employees/__init__.py
Normal file
3
backend/app/payroll/employees/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
従業員管理 (Employee Management)
|
||||
"""
|
||||
196
backend/app/payroll/employees/router.py
Normal file
196
backend/app/payroll/employees/router.py
Normal file
@@ -0,0 +1,196 @@
|
||||
"""
|
||||
従業員管理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()
|
||||
109
backend/app/payroll/employees/schemas.py
Normal file
109
backend/app/payroll/employees/schemas.py
Normal file
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
従業員管理用のスキーマ定義 (Employee Schemas)
|
||||
"""
|
||||
from pydantic import BaseModel, EmailStr
|
||||
from datetime import date, datetime
|
||||
from typing import Optional
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
class DependentBase(BaseModel):
|
||||
"""扶養家族の基本情報"""
|
||||
name: str
|
||||
relationship: str # 配偶者/子/親/その他
|
||||
birth_date: Optional[date] = None
|
||||
is_spouse: bool = False
|
||||
is_disabled: bool = False
|
||||
income_amount: Decimal = Decimal("0")
|
||||
valid_from: date
|
||||
valid_to: Optional[date] = None
|
||||
|
||||
|
||||
class DependentCreate(DependentBase):
|
||||
"""扶養家族の作成"""
|
||||
pass
|
||||
|
||||
|
||||
class DependentUpdate(BaseModel):
|
||||
"""扶養家族の更新"""
|
||||
name: Optional[str] = None
|
||||
relationship: Optional[str] = None
|
||||
birth_date: Optional[date] = None
|
||||
is_spouse: Optional[bool] = None
|
||||
is_disabled: Optional[bool] = None
|
||||
income_amount: Optional[Decimal] = None
|
||||
valid_from: Optional[date] = None
|
||||
valid_to: Optional[date] = None
|
||||
|
||||
|
||||
class Dependent(DependentBase):
|
||||
"""扶養家族の情報"""
|
||||
dependent_id: int
|
||||
employee_id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class EmployeeBase(BaseModel):
|
||||
"""従業員の基本情報"""
|
||||
employee_code: str
|
||||
name: str
|
||||
name_kana: Optional[str] = None
|
||||
birth_date: Optional[date] = None
|
||||
gender: Optional[str] = None
|
||||
email: Optional[EmailStr] = None
|
||||
phone: Optional[str] = None
|
||||
postal_code: Optional[str] = None
|
||||
address: Optional[str] = None
|
||||
hire_date: date
|
||||
termination_date: Optional[date] = None
|
||||
is_active: bool = True
|
||||
bank_name: Optional[str] = None
|
||||
bank_branch: Optional[str] = None
|
||||
bank_account_number: Optional[str] = None
|
||||
bank_account_type: Optional[str] = None # 普通/当座
|
||||
notes: Optional[str] = None
|
||||
|
||||
|
||||
class EmployeeCreate(EmployeeBase):
|
||||
"""従業員の作成"""
|
||||
pass
|
||||
|
||||
|
||||
class EmployeeUpdate(BaseModel):
|
||||
"""従業員の更新"""
|
||||
employee_code: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
name_kana: Optional[str] = None
|
||||
birth_date: Optional[date] = None
|
||||
gender: Optional[str] = None
|
||||
email: Optional[EmailStr] = None
|
||||
phone: Optional[str] = None
|
||||
postal_code: Optional[str] = None
|
||||
address: Optional[str] = None
|
||||
hire_date: Optional[date] = None
|
||||
termination_date: Optional[date] = None
|
||||
is_active: Optional[bool] = None
|
||||
bank_name: Optional[str] = None
|
||||
bank_branch: Optional[str] = None
|
||||
bank_account_number: Optional[str] = None
|
||||
bank_account_type: Optional[str] = None
|
||||
notes: Optional[str] = None
|
||||
|
||||
|
||||
class Employee(EmployeeBase):
|
||||
"""従業員の情報"""
|
||||
employee_id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class EmployeeWithDependents(Employee):
|
||||
"""扶養家族を含む従業員情報"""
|
||||
dependents: list[Dependent] = []
|
||||
Reference in New Issue
Block a user