This commit is contained in:
admin
2026-03-03 00:01:43 +09:00
parent 3b028b8fc0
commit 068903b933
31 changed files with 3204 additions and 889 deletions

View File

@@ -1,4 +1,5 @@
# backend/app/modules/trial_balance/service.py
# 試算表.pdf フォーマットに完全準拠した試算表サービス
from decimal import Decimal
from typing import Dict, Any, List
from app.core.database import get_connection
@@ -6,30 +7,9 @@ from app.core.database import get_connection
def D(x) -> Decimal:
return Decimal(str(x or 0))
# 科目グループ定義(表示順序を制御)- PDFフォーマットに準拠
ACCOUNT_GROUPS = [
# 資産の部
("現金・預金合計", ["101", "102", "103", "104", "105"]),
("売上債権合計", ["201", "202"]),
("有価証券合計", ["301"]),
("棚卸資産合計", ["210"]),
("他流動資産合計", ["203", "204", "205", "206", "207", "208"]),
("流動資産合計", ["101", "102", "103", "104", "105", "201", "202", "203", "204", "205", "206", "207", "208", "210", "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", "105", "201", "202", "203", "204", "205", "206", "207", "208", "210", "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"])
]
# 除外する科目コード
EXCLUDED_CODES = {'512', '513', '516'}
def fetch_trial_balance(date_from: str, date_to: str):
@@ -44,7 +24,6 @@ def fetch_trial_balance(date_from: str, date_to: str):
""")
accounts = cur.fetchall()
# 科目データを格納
account_data: Dict[str, Dict[str, Any]] = {}
@@ -52,8 +31,11 @@ def fetch_trial_balance(date_from: str, date_to: str):
aid = acc["account_id"]
code = acc["account_code"]
# 除外科目をスキップ
if code in EXCLUDED_CODES:
continue
# 期首残高: opening_balances テーブルの残高 + date_from より前の仕訳
# まず opening_balances テーブルから取得
cur.execute("""
SELECT COALESCE(opening_debit, 0) - COALESCE(opening_credit, 0) AS ob
FROM opening_balances
@@ -64,7 +46,6 @@ def fetch_trial_balance(date_from: str, date_to: str):
opening_balance_from_table = D(ob_row["ob"]) if ob_row else D(0)
# date_from より前の仕訳による増減
# 修正された仕訳is_latest = falseは除外
cur.execute("""
SELECT COALESCE(SUM(l.debit - l.credit), 0) AS opening
FROM journal_lines l
@@ -76,12 +57,10 @@ def fetch_trial_balance(date_from: str, date_to: str):
AND j.is_latest = true
""", (aid, date_from))
opening_from_journals = D(cur.fetchone()["opening"])
# 合計
opening = opening_balance_from_table + opening_from_journals
# 当期増減date_from date_to
# 修正された仕訳is_latest = falseは除外
cur.execute("""
SELECT
COALESCE(SUM(l.debit), 0) AS debit,
@@ -97,10 +76,10 @@ def fetch_trial_balance(date_from: str, date_to: str):
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)
@@ -116,182 +95,441 @@ def fetch_trial_balance(date_from: str, date_to: str):
"closing_balance": closing,
}
# 結果リスト(グループ化)
result_accounts: List[Dict[str, Any]] = []
# 既に挿入された合計行を追跡
inserted_totals = set()
# 大区分の合計を保存(資産・負債・純資産の総合計)
grand_total_closing = D(0)
# =============================================
# ヘルパー関数
# =============================================
def acct_row(code):
"""個別科目の行を返す存在しない場合はNone"""
if code not in account_data:
return None
d = account_data[code]
return {
"account_code": d["account_code"],
"account_name": d["account_name"],
"account_type": d["account_type"],
"opening_balance": str(d["opening_balance"]),
"debit": str(d["debit"]),
"credit": str(d["credit"]),
"closing_balance": str(d["closing_balance"]),
"ratio": None,
"is_total": False,
}
# 個別科目を追加
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"]
def total_row(name, codes, is_total=True, indent=False):
"""合計行を生成"""
existing = [c for c in codes if c in account_data]
opening = sum(account_data[c]["opening_balance"] for c in existing) if existing else D(0)
debit = sum(account_data[c]["debit"] for c in existing) if existing else D(0)
credit = sum(account_data[c]["credit"] for c in existing) if existing else D(0)
closing = sum(account_data[c]["closing_balance"] for c in existing) if existing else D(0)
return {
"account_code": "",
"account_name": name,
"account_type": "",
"opening_balance": str(opening),
"debit": str(debit),
"credit": str(credit),
"closing_balance": str(closing),
"ratio": None,
"is_total": is_total,
"indent": indent,
}
# 利益剰余金602の後に詳細を挿入
if code == "602":
# 繰越利益剰余金 = 602の期首残高
retained_earnings_opening = data["opening_balance"]
# 当期純損益金額を計算(収益 - 費用)
# 収益7xxの合計
revenue_total = D(0)
for acc_code, acc_data in account_data.items():
if acc_code.startswith("7"):
# 収益は貸方が増加、借方が減少
revenue_total += acc_data["credit"] - acc_data["debit"]
# 費用8xxの合計
expense_total = D(0)
for acc_code, acc_data in account_data.items():
if acc_code.startswith("8"):
# 費用は借方が増加、貸方が減少
expense_total += acc_data["debit"] - acc_data["credit"]
# 当期純損益金額 = 収益 - 費用
net_income = revenue_total - expense_total
# 繰越利益剰余金合計 = 期首繰越利益 + 当期純損益
retained_earnings_total = retained_earnings_opening + net_income
# 繰越利益の行を追加
result_accounts.append({
"account_code": "",
"account_name": "繰越利益",
"account_type": "equity",
"opening_balance": str(retained_earnings_opening),
"debit": "0",
"credit": "0",
"closing_balance": str(retained_earnings_opening),
"ratio": None,
"is_total": False,
"indent": True
})
# 当期純損益金額の行を追加
result_accounts.append({
"account_code": "",
"account_name": "当期純損益金額",
"account_type": "equity",
"opening_balance": "0",
"debit": str(expense_total) if expense_total > 0 else "0",
"credit": str(revenue_total) if revenue_total > 0 else "0",
"closing_balance": str(net_income),
"ratio": None,
"is_total": False,
"indent": True
})
# 繰越利益剰余金合計の行を追加
result_accounts.append({
"account_code": "",
"account_name": "繰越利益剰余金合計",
"account_type": "equity",
"opening_balance": str(retained_earnings_opening),
"debit": str(expense_total) if expense_total > 0 else "0",
"credit": str(revenue_total) if revenue_total > 0 else "0",
"closing_balance": str(retained_earnings_total),
"ratio": None,
"is_total": True,
"indent": True
})
# その他利益剰余金合計現在は0
result_accounts.append({
"account_code": "",
"account_name": "その他利益剰余金合計",
"account_type": "equity",
"opening_balance": "0",
"debit": "0",
"credit": "0",
"closing_balance": "0",
"ratio": None,
"is_total": True,
"indent": True
})
# 利益剰余金合計の行を追加
result_accounts.append({
"account_code": "",
"account_name": "利益剰余金合計",
"account_type": "equity",
"opening_balance": str(retained_earnings_opening),
"debit": str(expense_total) if expense_total > 0 else "0",
"credit": str(revenue_total) if revenue_total > 0 else "0",
"closing_balance": str(retained_earnings_total),
"ratio": None,
"is_total": True
})
def custom_row(name, opening, debit, credit, closing,
is_total=False, indent=False, account_type="", is_pl=False):
"""カスタム行を生成"""
row = {
"account_code": "",
"account_name": name,
"account_type": account_type,
"opening_balance": str(opening),
"debit": str(debit),
"credit": str(credit),
"closing_balance": str(closing),
"ratio": None,
"is_total": is_total,
"indent": indent,
}
if is_pl:
row["is_pl"] = True
return row
# 合計行を挿入(グループ定義の順序で)
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
# =============================================
# 結果リスト構築(試算表.pdf の順序に完全準拠)
# =============================================
result_accounts: List[Dict[str, Any]] = []
added_codes = set() # 追加済みの科目コードを追跡
def add(code):
"""個別科目を追加"""
r = acct_row(code)
if r:
result_accounts.append(r)
added_codes.add(code)
def add_total(name, codes, **kwargs):
"""合計行を追加"""
result_accounts.append(total_row(name, codes, **kwargs))
# ========== 資産の部 ==========
# 現金・預金
cash_codes = ["101", "102", "103", "104", "105"]
for c in cash_codes:
add(c)
add_total("現金・預金合計", cash_codes)
# 売上債権
receivable_codes = ["201", "202"]
for c in receivable_codes:
add(c)
add_total("売上債権合計", receivable_codes)
# 他流動資産
other_current_codes = ["203", "204", "205", "206", "207", "208"]
for c in other_current_codes:
add(c)
add_total("他流動資産合計", other_current_codes)
# 有価証券
securities_codes = ["301"]
for c in securities_codes:
add(c)
add_total("有価証券合計", securities_codes)
# 流動資産合計
current_asset_codes = cash_codes + receivable_codes + other_current_codes + securities_codes
add_total("流動資産合計", current_asset_codes)
# 有形固定資産
tangible_codes = ["401", "402", "403", "404", "405", "406", "407"]
for c in tangible_codes:
add(c)
add_total("有形固定資産合計", tangible_codes)
# 無形固定資産
intangible_codes = ["408"]
for c in intangible_codes:
add(c)
add_total("無形固定資産合計", intangible_codes)
# 投資その他の資産
investment_codes = ["410"]
for c in investment_codes:
add(c)
add_total("投資その他の資産合計", investment_codes)
# 固定資産合計
fixed_asset_codes = tangible_codes + intangible_codes + investment_codes
add_total("固定資産合計", fixed_asset_codes)
# 資産合計
all_asset_codes = current_asset_codes + fixed_asset_codes
add_total("資産合計", all_asset_codes)
# ========== 負債の部 ==========
# 仕入債務
trade_payable_codes = ["501"]
for c in trade_payable_codes:
add(c)
add_total("仕入債務合計", trade_payable_codes)
# 流動負債その他502-508
current_liability_other = ["502", "503", "504", "505", "506", "507", "508"]
for c in current_liability_other:
add(c)
current_liability_codes = trade_payable_codes + current_liability_other
add_total("流動負債合計", current_liability_codes)
# 固定負債
fixed_liability_codes = ["509"]
for c in fixed_liability_codes:
add(c)
add_total("固定負債合計", fixed_liability_codes)
# 負債合計
all_liability_codes = current_liability_codes + fixed_liability_codes
add_total("負債合計", all_liability_codes)
# ========== 510/511: 独立科目(負債と資本の間) ==========
add("510")
add("511")
# ========== 資本の部 ==========
# 資本金
capital_codes = ["601"]
for c in capital_codes:
add(c)
add_total("資本金合計", capital_codes)
# 繰越利益剰余金602
add("602")
# --- 当期純損益金額の計算 ---
revenue_total = D(0)
for acc_code, acc_data in account_data.items():
if acc_code.startswith("7"):
revenue_total += acc_data["credit"] - acc_data["debit"]
expense_total = D(0)
for acc_code, acc_data in account_data.items():
if acc_code.startswith("8"):
expense_total += acc_data["debit"] - acc_data["credit"]
net_income = revenue_total - expense_total
# 繰越利益剰余金の期首残高
retained_opening = account_data.get("602", {}).get("opening_balance", D(0))
retained_total = retained_opening + net_income
# 繰越利益
result_accounts.append(
custom_row("繰越利益", retained_opening, D(0), D(0), retained_opening,
indent=True, account_type="equity")
)
# 繰越利益剰余金合計
result_accounts.append(
custom_row("繰越利益剰余金合計", retained_opening,
abs(net_income) if net_income < 0 else D(0),
net_income if net_income > 0 else D(0),
retained_total,
is_total=True, indent=True, account_type="equity")
)
# その他利益剰余金合計
result_accounts.append(
custom_row("その他利益剰余金合計", D(0), D(0), D(0), D(0),
is_total=True, indent=True, account_type="equity")
)
# 利益剰余金合計
result_accounts.append(
custom_row("利益剰余金合計", retained_opening,
abs(net_income) if net_income < 0 else D(0),
net_income if net_income > 0 else D(0),
retained_total,
is_total=True, account_type="equity")
)
# 純資産合計
capital_closing = account_data.get("601", {}).get("closing_balance", D(0))
equity_total_closing = capital_closing + retained_total
equity_total_opening = sum(
account_data[c]["opening_balance"] for c in ["601", "602"] if c in account_data
)
result_accounts.append(
custom_row("純資産合計", equity_total_opening, D(0), D(0), equity_total_closing,
is_total=True, account_type="equity")
)
# ========== 損益の部(截図フォーマットに準拠) ==========
# --- P/L 用ヘルパー ---
def pl_row(code, indent=False):
"""P/L個別科目行closing = credit-debit or debit-credit"""
if code not in account_data:
return None
d = account_data[code]
if code.startswith("7"):
pl_closing = d["credit"] - d["debit"]
else:
pl_closing = d["debit"] - d["credit"]
return {
"account_code": d["account_code"],
"account_name": d["account_name"],
"account_type": d["account_type"],
"opening_balance": "0",
"debit": str(d["debit"]),
"credit": str(d["credit"]),
"closing_balance": str(pl_closing),
"ratio": None,
"is_total": False,
"indent": indent,
"is_pl": True,
}
def add_pl(code, indent=False):
r = pl_row(code, indent)
if r:
result_accounts.append(r)
added_codes.add(code)
# === 売上高 ===
add_pl("701")
# 売上高合計
rev_d = account_data.get("701", {})
revenue_val = rev_d.get("credit", D(0)) - rev_d.get("debit", D(0)) if "701" in account_data else D(0)
result_accounts.append(
custom_row("売上高合計", D(0), D(0), revenue_val, revenue_val, is_total=True, is_pl=True)
)
# === 売上原価セクション ===
# 期首商品棚卸高
result_accounts.append(
custom_row("期首商品棚卸高", D(0), D(0), D(0), D(0), indent=True, is_pl=True)
)
# 当期商品仕入高(= 801 仕入高)
if "801" in account_data:
add_pl("801", indent=True)
else:
result_accounts.append(
custom_row("当期商品仕入高", D(0), D(0), D(0), D(0), indent=True, is_pl=True)
)
added_codes.add("801")
# 合計
cogs_d = account_data.get("801", {})
cogs_debit = cogs_d.get("debit", D(0)) if "801" in account_data else D(0)
cogs_credit = cogs_d.get("credit", D(0)) if "801" in account_data else D(0)
cogs_net = cogs_debit - cogs_credit
result_accounts.append(
custom_row("合計", D(0), cogs_debit, cogs_credit, cogs_net,
is_total=True, indent=True, is_pl=True)
)
# 期末商品棚卸高
result_accounts.append(
custom_row("期末商品棚卸高", D(0), D(0), D(0), D(0), indent=True, is_pl=True)
)
# 売上原価
result_accounts.append(
custom_row("売上原価", D(0), cogs_debit, cogs_credit, cogs_net,
is_total=True, indent=True, is_pl=True)
)
# === 売上総損益金額 ===
gross_profit = revenue_val - cogs_net
result_accounts.append(
custom_row("売上総損益金額", D(0), D(0), gross_profit, gross_profit,
is_total=True, is_pl=True)
)
# === 販売管理費(截図の順序に準拠) ===
sga_order = [
"812", "813", "814", "815", # 人件費系
"802", # 外注費
"816", "817", # 交際費・会議費
"803", "804", "805", # 旅費・通信・消耗品
"818", # 新聞図書費
"808", # 支払手数料
"806", # 地代家賃
"819", "820", # 保険料・租税公課
]
# 上記リストにない8xx費用科目を末尾に追加801除外
remaining_8xx = [c for c in sorted(account_data.keys())
if c.startswith("8") and c != "801" and c not in sga_order]
sga_all = sga_order + remaining_8xx
sga_debit_total = D(0)
sga_credit_total = D(0)
sga_closing_total = D(0)
for code in sga_all:
add_pl(code, indent=True)
if code in account_data:
d = account_data[code]
sga_debit_total += d["debit"]
sga_credit_total += d["credit"]
sga_closing_total += d["debit"] - d["credit"]
# 販売管理費計
result_accounts.append(
custom_row("販売管理費計", D(0), sga_debit_total, sga_credit_total,
sga_closing_total, is_total=True, is_pl=True)
)
# === 営業損益金額 ===
operating_profit = gross_profit - sga_closing_total
result_accounts.append(
custom_row("営業損益金額", D(0), D(0), operating_profit, operating_profit,
is_total=True, is_pl=True)
)
# === 営業外収益 ===
non_op_rev_codes = ["702", "709"]
non_op_rev_debit = D(0)
non_op_rev_credit = D(0)
for code in non_op_rev_codes:
add_pl(code, indent=True)
if code in account_data:
d = account_data[code]
non_op_rev_debit += d["debit"]
non_op_rev_credit += d["credit"]
non_op_rev_closing = non_op_rev_credit - non_op_rev_debit
# 営業外収益合計
result_accounts.append(
custom_row("営業外収益合計", D(0), non_op_rev_debit, non_op_rev_credit,
non_op_rev_closing, is_total=True, indent=True, is_pl=True)
)
# 営業外費用合計(現在は該当科目なし)
result_accounts.append(
custom_row("営業外費用合計", D(0), D(0), D(0), D(0),
is_total=True, indent=True, is_pl=True)
)
# === 経常損益金額 ===
ordinary_profit = operating_profit + non_op_rev_closing
result_accounts.append(
custom_row("経常損益金額", D(0), D(0), ordinary_profit, ordinary_profit,
is_total=True, is_pl=True)
)
# === 特別利益合計 ===
result_accounts.append(
custom_row("特別利益合計", D(0), D(0), D(0), D(0),
is_total=True, indent=True, is_pl=True)
)
# === 特別損失合計 ===
result_accounts.append(
custom_row("特別損失合計", D(0), D(0), D(0), D(0),
is_total=True, indent=True, is_pl=True)
)
# === 当期純損益金額 ===
result_accounts.append(
custom_row("当期純損益金額", D(0), D(0), ordinary_profit, ordinary_profit,
is_total=True, is_pl=True)
)
# ========== 未分類科目(上記にない科目を末尾に追加) ==========
for code in sorted(account_data.keys()):
if code not in added_codes:
add(code)
# =============================================
# 構成比を計算
# B/S: 借方残合計ベース
# P/L: 売上高(対売上比)ベース
# =============================================
debit_balance_total = sum(
d["closing_balance"] for d in account_data.values()
if d["closing_balance"] > 0
)
for acc in result_accounts:
closing = D(acc["closing_balance"])
if acc.get("is_pl"):
# P/L: 対売上比
if revenue_val != 0:
ratio = (closing / revenue_val) * 100
acc["ratio"] = f"{ratio:.2f}"
else:
acc["ratio"] = "0.00"
else:
# B/S: 借方残合計ベース
if debit_balance_total != 0:
ratio = (closing / debit_balance_total) * 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())
# 総合計を計算
total_opening = sum(d["opening_balance"] for d in account_data.values())
total_debit = sum(d["debit"] for d in account_data.values())
total_credit = sum(d["credit"] for d in account_data.values())
total_closing = sum(d["closing_balance"] for d in account_data.values())
result = {
"totals": {
@@ -302,7 +540,7 @@ def fetch_trial_balance(date_from: str, date_to: str):
"ratio": "100.00"
},
"accounts": result_accounts,
"grand_total_closing": str(grand_total_closing)
"grand_total_closing": str(debit_balance_total),
}
return result

View File

@@ -40,6 +40,7 @@ def get_trial_balance(
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
@@ -55,6 +56,7 @@ def get_trial_balance(
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
@@ -83,7 +85,11 @@ def get_trial_balance(
a.account_name,
a.account_type
ORDER BY
a.account_code
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:

View File

@@ -0,0 +1,113 @@
#!/usr/bin/env python3
"""
フロントエンドの検索結果と実際のDB内容を比較
"""
import sys
sys.path.insert(0, '/app')
from app.db import get_connection
from_date = '2025-06-01'
to_date = '2025-06-30'
account_id = 501
print("=" * 80)
print("【検索条件】")
print(f" 期間: {from_date} {to_date}")
print(f" 科目: {account_id} (費用金)")
print("=" * 80)
with get_connection() as conn:
with conn.cursor() as cur:
# 修正履歴を含まない(最新版本のみ)
print("\n【1】修正履歴を含まないis_latest = true")
sql1 = """
SELECT
je.journal_entry_id,
je.entry_date,
je.description,
je.revision_count,
je.is_latest,
COALESCE(SUM(jl.debit), 0) as debit_total,
COALESCE(SUM(jl.credit), 0) as credit_total
FROM journal_entries je
LEFT JOIN journal_lines jl ON je.journal_entry_id = jl.journal_entry_id
WHERE je.is_deleted = false
AND je.is_latest = true
AND je.entry_date >= %s
AND je.entry_date <= %s
AND je.journal_entry_id IN (
SELECT DISTINCT journal_entry_id
FROM journal_lines
WHERE account_id = %s
)
GROUP BY je.journal_entry_id, je.entry_date, je.description, je.revision_count, je.is_latest
ORDER BY je.entry_date DESC, je.journal_entry_id DESC
"""
cur.execute(sql1, [from_date, to_date, account_id])
results1 = cur.fetchall()
print(f" 件数: {len(results1)}")
for row in results1:
print(f" • ID {row['journal_entry_id']}: {row['entry_date']} - {row['description']} (v{row['revision_count']}, latest={row['is_latest']})")
# 修正履歴を含む(すべてのバージョン)
print("\n【2】修正履歴を含むすべてのバージョン")
sql2 = """
SELECT
je.journal_entry_id,
je.entry_date,
je.description,
je.revision_count,
je.is_latest,
je.original_entry_id,
COALESCE(SUM(jl.debit), 0) as debit_total,
COALESCE(SUM(jl.credit), 0) as credit_total
FROM journal_entries je
LEFT JOIN journal_lines jl ON je.journal_entry_id = jl.journal_entry_id
WHERE je.is_deleted = false
AND je.entry_date >= %s
AND je.entry_date <= %s
AND je.journal_entry_id IN (
SELECT DISTINCT journal_entry_id
FROM journal_lines
WHERE account_id = %s
)
GROUP BY je.journal_entry_id, je.entry_date, je.description, je.revision_count, je.is_latest, je.original_entry_id
ORDER BY je.entry_date DESC, je.journal_entry_id DESC
"""
cur.execute(sql2, [from_date, to_date, account_id])
results2 = cur.fetchall()
print(f" 件数: {len(results2)}")
for row in results2:
print(f" • ID {row['journal_entry_id']}: {row['entry_date']} - {row['description']} (v{row['revision_count']}, latest={row['is_latest']}, orig={row['original_entry_id']})")
# 修正チェーンの分析
print("\n【3】修正チェーンの詳細分析")
sql3 = """
SELECT
COALESCE(je.original_entry_id, je.journal_entry_id) as chain_id,
COUNT(*) as version_count,
STRING_AGG(DISTINCT je.journal_entry_id::text, ', ') as entry_ids,
STRING_AGG(DISTINCT je.revision_count::text, ', ' ORDER BY je.revision_count::text DESC) as revisions,
MAX(je.revision_count) as max_revision
FROM journal_entries je
WHERE je.is_deleted = false
AND je.entry_date >= %s
AND je.entry_date <= %s
AND je.journal_entry_id IN (
SELECT DISTINCT journal_entry_id
FROM journal_lines
WHERE account_id = %s
)
GROUP BY COALESCE(je.original_entry_id, je.journal_entry_id)
ORDER BY chain_id DESC
"""
cur.execute(sql3, [from_date, to_date, account_id])
chains = cur.fetchall()
print(f" チェーン数: {len(chains)}")
for chain in chains:
print(f" • Chain {chain['chain_id']}: {chain['version_count']} バージョン (IDs: {chain['entry_ids']}, v{chain['revisions']})")
print("\n" + "=" * 80)
print("✓ 分析完了")
print("=" * 80)