from fastapi import APIRouter, HTTPException from pydantic import BaseModel, field_validator from enum import Enum from psycopg.errors import UniqueViolation, CheckViolation, StringDataRightTruncation from app.core.database import get_connection router = APIRouter(prefix="/accounts", tags=["科目マスタ"]) class AccountType(str, Enum): asset = "asset" liability = "liability" equity = "equity" revenue = "revenue" expense = "expense" class AccountCreate(BaseModel): account_code: str account_name: str account_type: AccountType @field_validator("account_code") @classmethod def validate_code(cls, v: str): if not v.isdigit(): raise ValueError("科目コードは数字のみで入力してください") if len(v) > 20: raise ValueError("科目コードが長すぎます(20文字以内)") return v @field_validator("account_name") @classmethod def validate_name(cls, v: str): if not v.strip(): raise ValueError("科目名を入力してください") if len(v) > 100: raise ValueError("科目名が長すぎます(100文字以内)") return v @router.get("/", summary="科目一覧取得") def list_accounts(): with get_connection() as conn, conn.cursor() as cur: cur.execute(""" SELECT account_id, account_code, account_name, account_type, is_active FROM accounts ORDER BY account_code """) return cur.fetchall() @router.post("/", summary="科目登録", status_code=201) def create_account(data: AccountCreate): try: with get_connection() as conn, conn.cursor() as cur: cur.execute(""" INSERT INTO accounts (account_code, account_name, account_type) VALUES (%s, %s, %s) RETURNING account_id """, (data.account_code, data.account_name, data.account_type.value)) new_id = cur.fetchone()["account_id"] conn.commit() return {"account_id": new_id} except UniqueViolation: # 409 也行;这里按你的要求统一 400 raise HTTPException(status_code=400, detail="この科目コードは既に登録されています。") except StringDataRightTruncation: raise HTTPException(status_code=400, detail="入力文字数が制限を超えています。") except CheckViolation: raise HTTPException(status_code=400, detail="入力値が不正です。")