from fastapi import APIRouter, HTTPException, Query from decimal import Decimal from app.core.database import get_connection router = APIRouter( prefix="/general-ledger", tags=["元帳"] ) def D(x) -> Decimal: return Decimal(str(x or 0)) @router.get("", summary="元帳取得(期首残高対応)") def get_general_ledger( account_id: int = Query(..., description="科目ID"), fiscal_year: int = Query(..., description="会計年度(例:2025)"), date_from: str = Query(..., description="YYYY-MM-DD"), date_to: str = Query(..., description="YYYY-MM-DD"), ): if date_from > date_to: raise HTTPException(status_code=400, detail="期間指定が不正です。") with get_connection() as conn, conn.cursor() as cur: # ---------------------------- # 科目存在確認 # ---------------------------- cur.execute(""" SELECT account_code, account_name FROM accounts WHERE account_id = %s AND is_active = true """, (account_id,)) acc = cur.fetchone() if not acc: raise HTTPException(status_code=400, detail="指定された科目は存在しません。") # ---------------------------- # 期首残高(opening_balances) # ---------------------------- cur.execute(""" SELECT COALESCE(opening_debit, 0) - COALESCE(opening_credit, 0) AS opening FROM opening_balances WHERE fiscal_year = %s AND account_id = %s """, (fiscal_year, account_id)) row = cur.fetchone() opening_balance = D(row["opening"]) if row else D(0) # ---------------------------- # 期間内仕訳取得 # ---------------------------- cur.execute(""" SELECT j.journal_date, j.description, l.debit, l.credit FROM journal_entries j JOIN journal_lines l ON l.journal_id = j.journal_id LEFT JOIN journal_entries child ON child.parent_entry_id = j.journal_id WHERE l.account_id = %s AND j.journal_date BETWEEN %s AND %s -- 逆仕訳を除外 AND j.description NOT LIKE '[逆仕訳]%' -- 子仕訳(修正後仕訳)を持つ修正前の仕訳を除外 AND child.journal_id IS NULL ORDER BY j.journal_date, l.line_id """, (account_id, date_from, date_to)) rows = cur.fetchall() # ---------------------------- # 累計残高計算 # ---------------------------- balance = opening_balance lines = [] for r in rows: debit = D(r["debit"]) credit = D(r["credit"]) balance += debit - credit lines.append({ "journal_date": r["journal_date"], "description": r["description"], "debit": str(debit), "credit": str(credit), "balance": str(balance), }) return { "account": { "account_id": account_id, "account_code": acc["account_code"], "account_name": acc["account_name"], }, "fiscal_year": fiscal_year, "period": { "from": date_from, "to": date_to }, "opening_balance": str(opening_balance), "lines": lines, "closing_balance": str(balance), }