109 lines
3.0 KiB
Python
109 lines
3.0 KiB
Python
from fastapi import APIRouter, HTTPException, Query
|
||
from pydantic import BaseModel
|
||
from typing import List
|
||
from decimal import Decimal
|
||
|
||
from app.core.database import get_connection
|
||
|
||
router = APIRouter(prefix="/opening-balances", tags=["期首残高"])
|
||
|
||
|
||
# ---------- Models ----------
|
||
|
||
class OpeningBalanceItem(BaseModel):
|
||
account_id: int
|
||
debit: Decimal = Decimal("0")
|
||
credit: Decimal = Decimal("0")
|
||
|
||
|
||
class OpeningBalanceRequest(BaseModel):
|
||
fiscal_year: int
|
||
balances: List[OpeningBalanceItem]
|
||
|
||
|
||
# ---------- GET:期首残高取得 ----------
|
||
|
||
@router.get("", summary="期首残高取得")
|
||
def get_opening_balances(
|
||
fiscal_year: int = Query(..., description="会計年度(例:2025)")
|
||
):
|
||
with get_connection() as conn, conn.cursor() as cur:
|
||
cur.execute("""
|
||
SELECT
|
||
a.account_id,
|
||
a.account_code,
|
||
a.account_name,
|
||
a.account_type,
|
||
COALESCE(o.opening_debit, 0) AS opening_debit,
|
||
COALESCE(o.opening_credit, 0) AS opening_credit
|
||
FROM accounts a
|
||
LEFT JOIN opening_balances o
|
||
ON o.account_id = a.account_id
|
||
AND o.fiscal_year = %s
|
||
WHERE a.is_active = true
|
||
ORDER BY a.account_code
|
||
""", (fiscal_year,))
|
||
return cur.fetchall()
|
||
|
||
|
||
# ---------- POST:期首残高保存 ----------
|
||
|
||
@router.post("", summary="期首残高保存")
|
||
def save_opening_balances(req: OpeningBalanceRequest):
|
||
|
||
# 年度锁定チェック
|
||
cur.execute("""
|
||
SELECT is_locked
|
||
FROM fiscal_year_locks
|
||
WHERE fiscal_year = %s
|
||
""", (req.fiscal_year,))
|
||
|
||
row = cur.fetchone()
|
||
if row and row["is_locked"]:
|
||
raise HTTPException(
|
||
status_code=400,
|
||
detail="この会計年度の期首残高は既に確定されています。"
|
||
)
|
||
|
||
total_debit = Decimal("0")
|
||
total_credit = Decimal("0")
|
||
|
||
for b in req.balances:
|
||
total_debit += b.debit
|
||
total_credit += b.credit
|
||
|
||
if total_debit != total_credit:
|
||
raise HTTPException(
|
||
status_code=400,
|
||
detail="借方合計と貸方合計が一致していません。"
|
||
)
|
||
|
||
with get_connection() as conn, conn.cursor() as cur:
|
||
|
||
# 既存データ削除(年度単位)
|
||
cur.execute("""
|
||
DELETE FROM opening_balances
|
||
WHERE fiscal_year = %s
|
||
""", (req.fiscal_year,))
|
||
|
||
# 再登録
|
||
for b in req.balances:
|
||
if b.debit == 0 and b.credit == 0:
|
||
continue
|
||
|
||
cur.execute("""
|
||
INSERT INTO opening_balances
|
||
(fiscal_year, account_id, opening_debit, opening_credit)
|
||
VALUES
|
||
(%s, %s, %s, %s)
|
||
""", (
|
||
req.fiscal_year,
|
||
b.account_id,
|
||
b.debit,
|
||
b.credit
|
||
))
|
||
|
||
conn.commit()
|
||
|
||
return {"message": "期首残高を保存しました。"}
|