Files
njts-accounting-core/backend/app/modules/trial_balance/service.py
2026-01-16 09:50:21 +09:00

214 lines
9.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# backend/app/modules/trial_balance/service.py
from decimal import Decimal
from typing import Dict, Any, List
from app.core.database import get_connection
def D(x) -> Decimal:
return Decimal(str(x or 0))
# 科目グループ定義(表示順序を制御)- PDFフォーマットに準拠
ACCOUNT_GROUPS = [
# 資産の部
("現金・預金合計", ["101", "102", "103", "104"]),
("流動資産合計", ["101", "102", "103", "104", "201", "202", "203", "204", "205"]),
("有価証券合計", ["301"]),
("有形固定資産合計", ["401", "402", "403", "404", "405", "406", "407"]),
("無形固定資産合計", ["408"]),
("投資その他の資産合計", ["301", "410"]),
("固定資産合計", ["301", "401", "402", "403", "404", "405", "406", "407", "408", "410"]),
("資産合計", ["101", "102", "103", "104", "201", "202", "203", "204", "205", "301", "401", "402", "403", "404", "405", "406", "407", "408", "410"]),
# 負債の部
("仕入債務合計", ["501"]),
("流動負債合計", ["501", "502", "503", "504", "505", "506", "507", "508"]),
("固定負債合計", ["509"]),
("負債合計", ["501", "502", "503", "504", "505", "506", "507", "508", "509"]),
# 資本の部
("資本金合計", ["601"]),
("利益剰余金合計", ["602"]),
("純資産合計", ["601", "602"])
]
def fetch_trial_balance(date_from: str, date_to: str):
with get_connection() as conn, conn.cursor() as cur:
# 勘定科目一覧
cur.execute("""
SELECT account_id, account_code, account_name, account_type
FROM accounts
WHERE is_active = true
ORDER BY account_code
""")
accounts = cur.fetchall()
# 科目データを格納
account_data: Dict[str, Dict[str, Any]] = {}
for acc in accounts:
aid = acc["account_id"]
code = acc["account_code"]
# 期首残高: opening_balances テーブルの残高 + date_from より前の仕訳
# まず opening_balances テーブルから取得
cur.execute("""
SELECT COALESCE(opening_debit, 0) - COALESCE(opening_credit, 0) AS ob
FROM opening_balances
WHERE account_id = %s
AND fiscal_year = EXTRACT(YEAR FROM %s::date)
""", (aid, date_from))
ob_row = cur.fetchone()
opening_balance_from_table = D(ob_row["ob"]) if ob_row else D(0)
# date_from より前の仕訳による増減
# 修正された仕訳(親となった仕訳)は除外
cur.execute("""
SELECT COALESCE(SUM(l.debit - l.credit), 0) AS opening
FROM journal_lines l
JOIN journal_entries j
ON j.journal_entry_id = l.journal_entry_id
LEFT JOIN journal_entries child
ON child.parent_entry_id = j.journal_entry_id
WHERE l.account_id = %s
AND j.entry_date < %s
AND j.is_deleted = false
AND j.description NOT LIKE '[逆仕訳]%%'
AND child.journal_entry_id IS NULL
""", (aid, date_from))
opening_from_journals = D(cur.fetchone()["opening"])
# 合計
opening = opening_balance_from_table + opening_from_journals
# 当期増減date_from date_to
# 修正された仕訳(親となった仕訳)は除外
cur.execute("""
SELECT
COALESCE(SUM(l.debit), 0) AS debit,
COALESCE(SUM(l.credit), 0) AS credit
FROM journal_lines l
JOIN journal_entries j
ON j.journal_entry_id = l.journal_entry_id
LEFT JOIN journal_entries child
ON child.parent_entry_id = j.journal_entry_id
WHERE l.account_id = %s
AND j.entry_date BETWEEN %s AND %s
AND j.is_deleted = false
AND j.description NOT LIKE '[逆仕訳]%%'
AND child.journal_entry_id IS NULL
""", (aid, date_from, date_to))
row = cur.fetchone()
debit = D(row["debit"])
credit = D(row["credit"])
closing = opening + debit - credit
# 負債・資本は絶対値で表示(見やすくするため)
if acc["account_type"] in ("liability", "equity"):
opening = abs(opening)
closing = abs(closing)
account_data[code] = {
"account_id": aid,
"account_code": code,
"account_name": acc["account_name"],
"account_type": acc["account_type"],
"opening_balance": opening,
"debit": debit,
"credit": credit,
"closing_balance": closing,
}
# 結果リスト(グループ化)
result_accounts: List[Dict[str, Any]] = []
# 既に挿入された合計行を追跡
inserted_totals = set()
# 大区分の合計を保存(資産・負債・純資産の総合計)
grand_total_closing = D(0)
# 個別科目を追加
for code in sorted(account_data.keys()):
data = account_data[code]
result_accounts.append({
"account_code": data["account_code"],
"account_name": data["account_name"],
"account_type": data["account_type"],
"opening_balance": str(data["opening_balance"]),
"debit": str(data["debit"]),
"credit": str(data["credit"]),
"closing_balance": str(data["closing_balance"]),
"ratio": None, # 後で計算
"is_total": False
})
# 大区分合計用に総計を積算
grand_total_closing += data["closing_balance"]
# 合計行を挿入(グループ定義の順序で)
for group_name, group_codes in ACCOUNT_GROUPS:
# このグループにまだ合計行を挿入していない
if group_name in inserted_totals:
continue
# 現在の科目がこのグループに含まれる
if code not in group_codes:
continue
# このグループの全科目を取得
group_codes_in_data = [gc for gc in group_codes if gc in account_data]
if not group_codes_in_data:
continue
# グループの最後の科目コードを取得
last_code_in_group = max(group_codes_in_data)
# 現在の科目がグループの最後の科目の場合
if code == last_code_in_group:
# グループ合計を計算
group_opening = sum(account_data[gc]["opening_balance"] for gc in group_codes_in_data)
group_debit = sum(account_data[gc]["debit"] for gc in group_codes_in_data)
group_credit = sum(account_data[gc]["credit"] for gc in group_codes_in_data)
group_closing = sum(account_data[gc]["closing_balance"] for gc in group_codes_in_data)
result_accounts.append({
"account_code": "",
"account_name": group_name,
"account_type": "",
"opening_balance": str(group_opening),
"debit": str(group_debit),
"credit": str(group_credit),
"closing_balance": str(group_closing),
"ratio": None, # 後で計算
"is_total": True
})
inserted_totals.add(group_name)
# 構成比を計算(総資産に対する割合)
for acc in result_accounts:
closing = D(acc["closing_balance"])
if grand_total_closing != 0:
ratio = (closing / grand_total_closing) * 100
acc["ratio"] = f"{ratio:.2f}"
else:
acc["ratio"] = "0.00"
# 総合計を計算
total_opening = sum(data["opening_balance"] for data in account_data.values())
total_debit = sum(data["debit"] for data in account_data.values())
total_credit = sum(data["credit"] for data in account_data.values())
total_closing = sum(data["closing_balance"] for data in account_data.values())
return {
"totals": {
"opening_balance": str(total_opening),
"debit": str(total_debit),
"credit": str(total_credit),
"closing_balance": str(total_closing),
"ratio": "100.00"
},
"accounts": result_accounts,
"grand_total_closing": str(grand_total_closing)
}