#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 検查和清理逆仕訳データと不正なis_latest標記 """ import psycopg from psycopg.rows import dict_row def check_and_cleanup(): try: conn = psycopg.connect( host="127.0.0.1", port=5432, dbname="njts_accounting", user="postgres", password="postgres", connect_timeout=3 ) with conn: with conn.cursor(row_factory=dict_row) as cur: print("=" * 80) print("【ステップ1】逆仕訳データの検出") print("=" * 80) # 検査是否存在通过反向分录来处理修改的数据 cur.execute(""" SELECT j1.journal_entry_id as original_id, j1.description as original_desc, j1.entry_date as original_date, j2.journal_entry_id as reverse_id, j2.description as reverse_desc, j2.entry_date as reverse_date FROM journal_entries j1 JOIN journal_lines l1 ON l1.journal_entry_id = j1.journal_entry_id JOIN journal_entries j2 ON j1.journal_entry_id = j2.parent_entry_id JOIN journal_lines l2 ON l2.journal_entry_id = j2.journal_entry_id AND l2.account_id = l1.account_id AND l2.debit = l1.credit AND l2.credit = l1.debit WHERE j1.is_deleted = false AND j2.is_deleted = false GROUP BY j1.journal_entry_id, j1.description, j1.entry_date, j2.journal_entry_id, j2.description, j2.entry_date LIMIT 20 """) reverse_entries = cur.fetchall() if reverse_entries: print(f"見つかった逆仕訳ペア: {len(reverse_entries)}") for entry in reverse_entries: print(f" 元:ID={entry['original_id']} ({entry['original_desc']}) {entry['original_date']}") print(f" 逆:ID={entry['reverse_id']} ({entry['reverse_desc']}) {entry['reverse_date']}") else: print("逆仕訳ペアが見つかりません") print() print("=" * 80) print("【ステップ2】is_latest=false だけど修正鎖に含まれない孤立記録") print("=" * 80) cur.execute(""" SELECT journal_entry_id, description, is_latest, is_deleted, parent_entry_id, original_entry_id, entry_date FROM journal_entries WHERE is_latest = false AND (original_entry_id IS NULL AND parent_entry_id IS NULL) AND is_deleted = false LIMIT 20 """) orphan_entries = cur.fetchall() if orphan_entries: print(f"孤立記録が見つかりました: {len(orphan_entries)}") for entry in orphan_entries: print(f" ID={entry['journal_entry_id']}: {entry['description']} (date={entry['entry_date']})") else: print("孤立記録が見つかりません") print() print("=" * 80) print("【ステップ3】修正鎖の整体性確認") print("=" * 80) # 检查是否有parent_entry_id指向不存在的記録 cur.execute(""" SELECT je.journal_entry_id, je.parent_entry_id, je.description FROM journal_entries je LEFT JOIN journal_entries parent ON parent.journal_entry_id = je.parent_entry_id WHERE je.parent_entry_id IS NOT NULL AND parent.journal_entry_id IS NULL LIMIT 20 """) broken_links = cur.fetchall() if broken_links: print(f"破损されたリンク が見つかりました: {len(broken_links)}") for entry in broken_links: print(f" ID={entry['journal_entry_id']}: 削除されたparen_entry_id={entry['parent_entry_id']}") else: print("破损されたリンクが見つかりません") print() print("=" * 80) print("【ステップ4】修正被修改的还有没有更新成正确is_latest的记录") print("=" * 80) # 检查是否有多个is_latest=true在同一修正鎖中 cur.execute(""" SELECT original_entry_id, COUNT(*) as total_count, SUM(CASE WHEN is_latest = true THEN 1 ELSE 0 END) as latest_count, SUM(CASE WHEN is_latest = false THEN 1 ELSE 0 END) as old_count FROM journal_entries WHERE original_entry_id IS NOT NULL GROUP BY original_entry_id HAVING SUM(CASE WHEN is_latest = true THEN 1 ELSE 0 END) != 1 """) bad_chains = cur.fetchall() if bad_chains: print(f"不正合な修正鎖が見つかりました: {len(bad_chains)}") for chain in bad_chains: print(f" original_id={chain['original_entry_id']}: 合計={chain['total_count']}, latest={chain['latest_count']}, old={chain['old_count']}") else: print("正常な修正鎖のみです") conn.close() return True except Exception as e: print(f"エラー: {e}") import traceback traceback.print_exc() return False if __name__ == "__main__": check_and_cleanup()