150 lines
5.0 KiB
Python
150 lines
5.0 KiB
Python
from fastapi import APIRouter, Query
|
||
from datetime import date
|
||
from typing import Optional
|
||
from app.core.database import get_connection
|
||
|
||
router = APIRouter(prefix="/trial-balance", tags=["试算表"])
|
||
|
||
|
||
@router.get("", summary="试算表(全科目)")
|
||
def get_trial_balance(
|
||
fiscal_year: Optional[int] = Query(None, description="会计年度(可选)"),
|
||
date_from: Optional[date] = Query(None, description="开始日期(可选)"),
|
||
date_to: Optional[date] = Query(None, description="结束日期(可选)"),
|
||
):
|
||
print("### trial-balance OPTIONAL PARAMS ACTIVE ###")
|
||
|
||
with get_connection() as conn:
|
||
# 检查 is_latest 字段是否存在(新的版本追踪系统)
|
||
with conn.cursor() as check_cur:
|
||
check_cur.execute("""
|
||
SELECT COUNT(*) as cnt
|
||
FROM information_schema.columns
|
||
WHERE table_name = 'journal_entries' AND column_name = 'is_latest'
|
||
""")
|
||
has_is_latest = check_cur.fetchone()[0] > 0
|
||
|
||
# 构建查询语句
|
||
if has_is_latest:
|
||
# 新系统:只取最新版本(is_latest = true)
|
||
sql = """
|
||
SELECT
|
||
a.account_id,
|
||
a.account_code,
|
||
a.account_name,
|
||
a.account_type,
|
||
COALESCE(SUM(l.debit), 0) AS debit,
|
||
COALESCE(SUM(l.credit), 0) AS credit
|
||
FROM journal_lines l
|
||
JOIN journal_entries e ON e.journal_entry_id = l.journal_entry_id
|
||
JOIN accounts a ON a.account_id = l.account_id
|
||
WHERE e.is_deleted = false
|
||
AND e.is_latest = true
|
||
AND a.account_code NOT IN ('512', '513', '516')
|
||
"""
|
||
else:
|
||
# 旧系统:排除已修正的记录(使用 parent_entry_id)
|
||
sql = """
|
||
SELECT
|
||
a.account_id,
|
||
a.account_code,
|
||
a.account_name,
|
||
a.account_type,
|
||
COALESCE(SUM(l.debit), 0) AS debit,
|
||
COALESCE(SUM(l.credit), 0) AS credit
|
||
FROM journal_lines l
|
||
JOIN journal_entries e ON e.journal_entry_id = l.journal_entry_id
|
||
JOIN accounts a ON a.account_id = l.account_id
|
||
WHERE e.is_deleted = false
|
||
AND a.account_code NOT IN ('512', '513', '516')
|
||
AND e.journal_entry_id NOT IN (
|
||
SELECT parent_entry_id
|
||
FROM journal_entries
|
||
WHERE parent_entry_id IS NOT NULL
|
||
)
|
||
"""
|
||
|
||
params = {}
|
||
|
||
if fiscal_year is not None:
|
||
sql += " AND e.fiscal_year = %(fiscal_year)s"
|
||
params["fiscal_year"] = fiscal_year
|
||
|
||
if date_from is not None:
|
||
sql += " AND e.entry_date >= %(date_from)s"
|
||
params["date_from"] = date_from
|
||
|
||
if date_to is not None:
|
||
sql += " AND e.entry_date <= %(date_to)s"
|
||
params["date_to"] = date_to
|
||
|
||
sql += """
|
||
GROUP BY
|
||
a.account_id,
|
||
a.account_code,
|
||
a.account_name,
|
||
a.account_type
|
||
ORDER BY
|
||
CASE
|
||
WHEN a.account_code < '509' THEN a.account_code
|
||
WHEN a.account_code IN ('510', '511') THEN '508.' || a.account_code
|
||
ELSE a.account_code
|
||
END
|
||
"""
|
||
|
||
with get_connection() as conn, conn.cursor() as cur:
|
||
cur.execute(sql, params)
|
||
rows = cur.fetchall()
|
||
|
||
return rows
|
||
|
||
|
||
@router.get("/fiscal-years", summary="仕訳データから会計年度一覧取得(5月期)")
|
||
def get_fiscal_years():
|
||
"""
|
||
仕訳の最小・最大日付から5月期の年度リストを生成して返す。
|
||
例: 2025-06-01〜2026-05-31 → {"label": "令和8年(2026年)5月期", "date_from": "2025-06-01", "date_to": "2026-05-31"}
|
||
"""
|
||
REIWA_OFFSET = 2018 # 令和元年 = 2019年
|
||
|
||
with get_connection() as conn, conn.cursor() as cur:
|
||
cur.execute("""
|
||
SELECT
|
||
MIN(entry_date)::date AS min_date,
|
||
MAX(entry_date)::date AS max_date
|
||
FROM journal_entries
|
||
WHERE is_deleted = false
|
||
""")
|
||
row = cur.fetchone()
|
||
|
||
if not row or not row["min_date"]:
|
||
return []
|
||
|
||
min_date = row["min_date"]
|
||
max_date = row["max_date"]
|
||
|
||
# 5月期の年度を計算: 決算年(end_year)は entry_date の月が6以上なら翌年、1〜5なら当年
|
||
def fiscal_end_year(d):
|
||
return d.year + 1 if d.month >= 6 else d.year
|
||
|
||
start_fy = fiscal_end_year(min_date)
|
||
end_fy = fiscal_end_year(max_date)
|
||
|
||
result = []
|
||
for fy in range(start_fy, end_fy + 1): # 古い順
|
||
reiwa = fy - REIWA_OFFSET
|
||
if reiwa > 0:
|
||
wareki = f"令和{reiwa}年"
|
||
elif reiwa == 0:
|
||
wareki = "平成31/令和元年"
|
||
else:
|
||
wareki = f"平成{fy - 1988}年"
|
||
label = f"{wareki}({fy}年)5月期"
|
||
result.append({
|
||
"label": label,
|
||
"date_from": f"{fy - 1}-06-01",
|
||
"date_to": f"{fy}-05-31",
|
||
})
|
||
|
||
return result
|