desktop修正
This commit is contained in:
27
backend/app/modules/opening_balances/lock_router.py
Normal file
27
backend/app/modules/opening_balances/lock_router.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from app.core.database import get_connection
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/opening-balances",
|
||||
tags=["期首残高"]
|
||||
)
|
||||
|
||||
class LockRequest(BaseModel):
|
||||
fiscal_year: int
|
||||
|
||||
@router.post("/lock", summary="期首残高年度確定")
|
||||
def lock_fiscal_year(req: LockRequest):
|
||||
|
||||
with get_connection() as conn, conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
INSERT INTO fiscal_year_locks (fiscal_year, is_locked)
|
||||
VALUES (%s, true)
|
||||
ON CONFLICT (fiscal_year)
|
||||
DO UPDATE SET
|
||||
is_locked = true,
|
||||
locked_at = CURRENT_TIMESTAMP
|
||||
""", (req.fiscal_year,))
|
||||
conn.commit()
|
||||
|
||||
return {"message": "期首残高を確定しました。"}
|
||||
@@ -1,55 +1,108 @@
|
||||
# backend/app/modules/opening_balances/router.py
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from psycopg.errors import ForeignKeyViolation
|
||||
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
|
||||
from app.modules.opening_balances.schema import OpeningBalanceCreate
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/opening-balances",
|
||||
tags=["期首残高"]
|
||||
)
|
||||
|
||||
@router.post("/", summary="期首残高 登録/更新")
|
||||
def upsert_opening_balance(data: OpeningBalanceCreate):
|
||||
try:
|
||||
with get_connection() as conn, conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
INSERT INTO opening_balances
|
||||
(fiscal_year, account_id, opening_debit, opening_credit)
|
||||
VALUES (%s, %s, %s, %s)
|
||||
ON CONFLICT (fiscal_year, account_id)
|
||||
DO UPDATE SET
|
||||
opening_debit = EXCLUDED.opening_debit,
|
||||
opening_credit = EXCLUDED.opening_credit
|
||||
""", (
|
||||
data.fiscal_year,
|
||||
data.account_id,
|
||||
data.opening_debit,
|
||||
data.opening_credit
|
||||
))
|
||||
conn.commit()
|
||||
return {"result": "ok"}
|
||||
|
||||
except ForeignKeyViolation:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="存在しない科目が指定されています"
|
||||
)
|
||||
router = APIRouter(prefix="/opening-balances", tags=["期首残高"])
|
||||
|
||||
|
||||
@router.get("/{fiscal_year}", summary="期首残高一覧取得")
|
||||
def list_opening_balances(fiscal_year: int):
|
||||
# ---------- 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,
|
||||
ob.opening_debit,
|
||||
ob.opening_credit
|
||||
FROM opening_balances ob
|
||||
JOIN accounts a ON a.account_id = ob.account_id
|
||||
WHERE ob.fiscal_year = %s
|
||||
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": "期首残高を保存しました。"}
|
||||
|
||||
Reference in New Issue
Block a user