diff --git a/backend/app/modules/opening_balances/lock_router.py b/backend/app/modules/opening_balances/lock_router.py index 527c2ed..33e6cf7 100644 --- a/backend/app/modules/opening_balances/lock_router.py +++ b/backend/app/modules/opening_balances/lock_router.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, HTTPException +from fastapi import APIRouter, HTTPException, Query from pydantic import BaseModel from app.core.database import get_connection @@ -10,6 +10,25 @@ router = APIRouter( class LockRequest(BaseModel): fiscal_year: int +@router.get("/lock-status", summary="期首残高確定状態確認") +def get_lock_status(fiscal_year: int = Query(..., description="会計年度")): + try: + with get_connection() as conn, conn.cursor() as cur: + cur.execute(""" + SELECT is_locked + FROM fiscal_year_locks + WHERE fiscal_year = %s + """, (fiscal_year,)) + row = cur.fetchone() + + if row: + return {"is_locked": row[0]} + else: + return {"is_locked": False} + except Exception: + # テーブルが存在しない場合は未ロック扱い + return {"is_locked": False} + @router.post("/lock", summary="期首残高年度確定") def lock_fiscal_year(req: LockRequest): diff --git a/backend/app/modules/trial_balance/service.py b/backend/app/modules/trial_balance/service.py index 5a3a54b..83bd5e7 100644 --- a/backend/app/modules/trial_balance/service.py +++ b/backend/app/modules/trial_balance/service.py @@ -6,10 +6,33 @@ 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 @@ -18,37 +41,50 @@ def fetch_trial_balance(date_from: str, date_to: str): """) accounts = cur.fetchall() - result_accounts: List[Dict[str, Any]] = [] - - total_opening = Decimal("0") - total_debit = Decimal("0") - total_credit = Decimal("0") - total_closing = Decimal("0") + # 科目データを格納 + 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_id = l.journal_id - AND j.is_deleted = false + JOIN journal_entries j + ON j.journal_entry_id = l.journal_entry_id WHERE l.account_id = %s - AND j.journal_date < %s + AND j.entry_date < %s + AND j.is_deleted = false """, (aid, date_from)) - opening = D(cur.fetchone()["opening"]) + 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.debit), 0) AS debit, COALESCE(SUM(l.credit), 0) AS credit FROM journal_lines l - JOIN journal_entries j ON j.journal_id = l.journal_id - AND j.is_deleted = false + JOIN journal_entries j + ON j.journal_entry_id = l.journal_entry_id WHERE l.account_id = %s - AND j.journal_date BETWEEN %s AND %s + AND j.entry_date BETWEEN %s AND %s + AND j.is_deleted = false """, (aid, date_from, date_to)) row = cur.fetchone() debit = D(row["debit"]) @@ -56,28 +92,107 @@ def fetch_trial_balance(date_from: str, date_to: str): closing = opening + debit - credit - total_opening += opening - total_debit += debit - total_credit += credit - total_closing += closing - - result_accounts.append({ + account_data[code] = { "account_id": aid, - "account_code": acc["account_code"], + "account_code": code, "account_name": acc["account_name"], "account_type": acc["account_type"], - "opening_balance": str(opening), - "debit": str(debit), - "credit": str(credit), - "closing_balance": str(closing), + "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) + "closing_balance": str(total_closing), + "ratio": "100.00" }, - "accounts": result_accounts + "accounts": result_accounts, + "grand_total_closing": str(grand_total_closing) } diff --git a/backend/app/routers/trial_balance.py b/backend/app/routers/trial_balance.py index 375d12b..5a6ffdd 100644 --- a/backend/app/routers/trial_balance.py +++ b/backend/app/routers/trial_balance.py @@ -23,7 +23,7 @@ def get_trial_balance( 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_id = l.journal_id + JOIN journal_entries e ON e.journal_entry_id = l.journal_entry_id JOIN accounts a ON a.account_id = l.account_id WHERE 1 = 1 """ diff --git a/backend/sql/insert_opening_balances_2024.sql b/backend/sql/insert_opening_balances_2024.sql new file mode 100644 index 0000000..5f985c9 --- /dev/null +++ b/backend/sql/insert_opening_balances_2024.sql @@ -0,0 +1,50 @@ +-- 2024年度(2024年6月1日~2025年5月31日)期首残高データ +-- ★残高試算表_20250724.pdfより + +-- 既存データを削除(2024年度のみ) +DELETE FROM opening_balances WHERE fiscal_year = 2024; + +-- 期首残高データの挿入 +INSERT INTO opening_balances (fiscal_year, account_id, opening_debit, opening_credit) VALUES +-- 現金(101) +(2024, (SELECT account_id FROM accounts WHERE account_code = '101'), 878375, 0), + +-- 普通預金(102)- UFJ/0788058 として登録 +(2024, (SELECT account_id FROM accounts WHERE account_code = '102'), 11246013, 0), + +-- 売掛金(201) +(2024, (SELECT account_id FROM accounts WHERE account_code = '201'), 5269872, 0), + +-- 仮払金(203) +(2024, (SELECT account_id FROM accounts WHERE account_code = '203'), 1284834, 0), + +-- 買掛金(501) +(2024, (SELECT account_id FROM accounts WHERE account_code = '501'), 0, 9933067), + +-- 未払金(502) +(2024, (SELECT account_id FROM accounts WHERE account_code = '502'), 0, 840000), + +-- 未払法人税等(504) +(2024, (SELECT account_id FROM accounts WHERE account_code = '504'), 0, 94700), + +-- 未払消費税等(505) +(2024, (SELECT account_id FROM accounts WHERE account_code = '505'), 0, 1498400), + +-- 預り金(506) +(2024, (SELECT account_id FROM accounts WHERE account_code = '506'), 0, 61300), + +-- 資本金(601) +(2024, (SELECT account_id FROM accounts WHERE account_code = '601'), 0, 6000000); + +-- 繰越利益剰余金(602)は自動計算されるため入力不要 + +-- 確認用クエリ +SELECT + a.account_code, + a.account_name, + ob.opening_debit, + ob.opening_credit +FROM opening_balances ob +JOIN accounts a ON ob.account_id = a.account_id +WHERE ob.fiscal_year = 2024 +ORDER BY a.account_code; diff --git a/backend/sql/insert_opening_balances_2025.sql b/backend/sql/insert_opening_balances_2025.sql new file mode 100644 index 0000000..fdc31f4 --- /dev/null +++ b/backend/sql/insert_opening_balances_2025.sql @@ -0,0 +1,67 @@ +-- 既存の期首残高データを削除 +DELETE FROM opening_balances; + +-- 2025年度(2025年6月1日~2026年5月31日)期首残高データ +-- ★残高試算表_20250724.pdf の「当期残高」列より(2025年5月31日時点) + +INSERT INTO opening_balances (fiscal_year, account_id, opening_debit, opening_credit) VALUES +-- 現金(101): 0 + +-- 普通預金(102): 3,231,129 +(2025, (SELECT account_id FROM accounts WHERE account_code = '102'), 3231129, 0), + +-- 売掛金(201): 3,083,000 +(2025, (SELECT account_id FROM accounts WHERE account_code = '201'), 3083000, 0), + +-- 未収入金(202): 17,842,822(短期貸付金の代わり) +(2025, (SELECT account_id FROM accounts WHERE account_code = '202'), 17842822, 0), + +-- 前渡金(203 仮払金): 387,400 +(2025, (SELECT account_id FROM accounts WHERE account_code = '203'), 387400, 0), + +-- 工具器具備品(406): 345,383 +(2025, (SELECT account_id FROM accounts WHERE account_code = '406'), 345383, 0), + +-- 差入保証金(410): 220,000 +(2025, (SELECT account_id FROM accounts WHERE account_code = '410'), 220000, 0), + +-- 買掛金(501): 9,376,260(貸方) +(2025, (SELECT account_id FROM accounts WHERE account_code = '501'), 0, 9376260), + +-- 未払金(502): 0 + +-- 未払費用(503): 1,087,312(貸方) +(2025, (SELECT account_id FROM accounts WHERE account_code = '503'), 0, 1087312), + +-- 未払法人税等(504): 1,525,500(貸方) +(2025, (SELECT account_id FROM accounts WHERE account_code = '504'), 0, 1525500), + +-- 未払消費税等(505): 1,245,200(貸方) +(2025, (SELECT account_id FROM accounts WHERE account_code = '505'), 0, 1245200), + +-- 預り金(506): 669,316(貸方) +(2025, (SELECT account_id FROM accounts WHERE account_code = '506'), 0, 669316), + +-- 資本金(601): 6,000,000(貸方) +(2025, (SELECT account_id FROM accounts WHERE account_code = '601'), 0, 6000000); + +-- 繰越利益剰余金(602): 5,206,146 は自動計算されるため入力不要 + +-- 確認用クエリ +SELECT + a.account_code, + a.account_name, + ob.opening_debit, + ob.opening_credit +FROM opening_balances ob +JOIN accounts a ON ob.account_id = a.account_id +WHERE ob.fiscal_year = 2025 +ORDER BY a.account_code; + +-- 合計確認 +SELECT + SUM(opening_debit) as total_debit, + SUM(opening_credit) as total_credit, + SUM(opening_debit) - SUM(opening_credit) as diff +FROM opening_balances +WHERE fiscal_year = 2025; diff --git a/frontend/journal-entry.html b/frontend/journal-entry.html index a687bc6..5d31e44 100644 --- a/frontend/journal-entry.html +++ b/frontend/journal-entry.html @@ -52,7 +52,13 @@
- +
@@ -60,9 +66,11 @@ +
@@ -100,6 +108,8 @@
+
+

月次锁定管理

@@ -148,6 +158,43 @@ 🔒 该月份已锁定,不能编辑仕訳
+ +
+ +

仕訳検索

+ +
+ + + ~ + + + + + + +
+ + + + + + + + + + + + +
日付摘要借方合計貸方合計操作
+ diff --git a/frontend/js/opening_balance.js b/frontend/js/opening_balance.js index 4c156b3..e02209d 100644 --- a/frontend/js/opening_balance.js +++ b/frontend/js/opening_balance.js @@ -2,24 +2,95 @@ let accounts = []; let retainedEarnings = null; let isLocked = false; +// 科目グループ定義(小計付き) +const GROUPS = [ + { + key: "asset", + label: "資産", + subGroups: [ + { label: "現金・預金合計", codes: ["101", "102", "103", "104"] }, + { label: "売上債権合計", codes: ["201"] }, + { + label: "流動資産合計", + codes: [ + "101", + "102", + "103", + "104", + "201", + "202", + "203", + "204", + "205", + "206", + ], + }, + { + label: "固定資産合計", + codes: [ + "301", + "401", + "402", + "403", + "404", + "405", + "406", + "407", + "408", + "410", + ], + }, + ], + }, + { + key: "liability", + label: "負債", + subGroups: [ + { label: "仕入債務合計", codes: ["501"] }, + { + label: "流動負債合計", + codes: ["501", "502", "503", "504", "505", "506", "507", "508"], + }, + { label: "固定負債合計", codes: ["509"] }, + ], + }, + { + key: "equity", + label: "純資産", + subGroups: [ + { label: "株主資本合計", codes: ["601", "602"] }, + { label: "純資産合計", codes: ["601", "602"] }, + ], + }, +]; + function formatNumber(n) { return Number(n || 0).toLocaleString(); } +function parseNumber(str) { + // カンマを除去して数値に変換 + return Number(String(str).replace(/,/g, "")) || 0; +} + function recalcTotals() { let debit = 0; let credit = 0; - // 先算:不含繰越利益 + // 先算:不含繰越利益剰余金 accounts.forEach((a) => { - if (a.account_name === "繰越利益") return; + if (a.account_name === "繰越利益剰余金" || a.account_name === "繰越利益") + return; debit += Number(a.opening_debit || 0); credit += Number(a.opening_credit || 0); }); const diff = debit - credit; - // 自动计算 繰越利益 + console.log("Debug: debit=", debit, "credit=", credit, "diff=", diff); + console.log("Debug: retainedEarnings=", retainedEarnings); + + // 自动计算 繰越利益剰余金 if (retainedEarnings) { if (diff > 0) { retainedEarnings.opening_debit = 0; @@ -28,6 +99,20 @@ function recalcTotals() { retainedEarnings.opening_debit = -diff; retainedEarnings.opening_credit = 0; } + console.log("Debug: retainedEarnings after calc=", retainedEarnings); + + // DOM入力フィールドを更新 + const retainedIndex = accounts.indexOf(retainedEarnings); + const inputs = document.querySelectorAll("#balanceTable tbody input"); + inputs.forEach((input) => { + const match = input + .getAttribute("onchange") + ?.match(/accounts\[(\d+)\]\.opening_(debit|credit)/); + if (match && parseInt(match[1]) === retainedIndex) { + const field = match[2]; // "debit" or "credit" + input.value = formatNumber(retainedEarnings[`opening_${field}`]); + } + }); } // 再算一次(包含繰越利益) @@ -44,6 +129,35 @@ function recalcTotals() { document.getElementById("diff").innerText = formatNumber( finalDebit - finalCredit ); + + // 小計行を更新 + updateSubtotalRows(); +} + +function updateSubtotalRows() { + GROUPS.forEach((group) => { + group.subGroups.forEach((subGroup) => { + const subGroupTotal = accounts + .filter((acc) => subGroup.codes.includes(acc.account_code)) + .reduce( + (sum, acc) => ({ + debit: sum.debit + Number(acc.opening_debit || 0), + credit: sum.credit + Number(acc.opening_credit || 0), + }), + { debit: 0, credit: 0 } + ); + + // 対応する小計行を探して更新 + const rows = document.querySelectorAll("#balanceTable tbody tr"); + rows.forEach((row) => { + const labelCell = row.cells[1]; + if (labelCell && labelCell.textContent === subGroup.label) { + row.cells[2].textContent = formatNumber(subGroupTotal.debit); + row.cells[3].textContent = formatNumber(subGroupTotal.credit); + } + }); + }); + }); } async function loadOpeningBalances() { @@ -52,7 +166,9 @@ async function loadOpeningBalances() { const data = await apiGet("/opening-balances", { fiscal_year: year }); accounts = data; - retainedEarnings = accounts.find((a) => a.account_name === "繰越利益"); + retainedEarnings = accounts.find( + (a) => a.account_name === "繰越利益剰余金" || a.account_name === "繰越利益" + ); const tbody = document.querySelector("#balanceTable tbody"); tbody.innerHTML = ""; @@ -67,28 +183,62 @@ async function loadOpeningBalances() { `; tbody.appendChild(headerTr); - accounts.forEach((a, i) => { - if (a.account_type !== group.key) return; + const groupAccounts = accounts.filter((a) => a.account_type === group.key); - const isRetained = a.account_name === "繰越利益"; + groupAccounts.forEach((a, idx) => { + const i = accounts.indexOf(a); + const isRetained = + a.account_name === "繰越利益剰余金" || a.account_name === "繰越利益"; const tr = document.createElement("tr"); tr.innerHTML = ` ${a.account_code} ${a.account_name} - + onchange="accounts[${i}].opening_debit=parseNumber(this.value); this.value=formatNumber(accounts[${i}].opening_debit); recalcTotals();"> - + onchange="accounts[${i}].opening_credit=parseNumber(this.value); this.value=formatNumber(accounts[${i}].opening_credit); recalcTotals();"> `; tbody.appendChild(tr); + + // 小計行の追加 + group.subGroups.forEach((subGroup) => { + const isLastInSubGroup = + subGroup.codes.includes(a.account_code) && + !groupAccounts + .slice(idx + 1) + .some((nextAcc) => subGroup.codes.includes(nextAcc.account_code)); + + if (isLastInSubGroup) { + const subGroupTotal = accounts + .filter((acc) => subGroup.codes.includes(acc.account_code)) + .reduce( + (sum, acc) => ({ + debit: sum.debit + Number(acc.opening_debit || 0), + credit: sum.credit + Number(acc.opening_credit || 0), + }), + { debit: 0, credit: 0 } + ); + + const totalTr = document.createElement("tr"); + totalTr.style.backgroundColor = "#d0d0d0"; + totalTr.style.fontWeight = "bold"; + totalTr.innerHTML = ` + + ${subGroup.label} + ${formatNumber(subGroupTotal.debit)} + ${formatNumber(subGroupTotal.credit)} + `; + tbody.appendChild(totalTr); + } + }); }); }); @@ -118,9 +268,10 @@ async function saveOpeningBalances() { const year = Number(document.getElementById("fiscalYear").value); const balances = accounts - // ❗ 排除 繰越利益 + // ❗ 排除 繰越利益剰余金 .filter( (a) => + a.account_name !== "繰越利益剰余金" && a.account_name !== "繰越利益" && (Number(a.opening_debit) !== 0 || Number(a.opening_credit) !== 0) ) diff --git a/frontend/js/trial-balance.js b/frontend/js/trial-balance.js index 92d8464..3b237c0 100644 --- a/frontend/js/trial-balance.js +++ b/frontend/js/trial-balance.js @@ -1,32 +1,178 @@ document.addEventListener("DOMContentLoaded", () => { console.log("trial-balance.js 已执行"); + const API_URL = "http://127.0.0.1:18080/trial-balance"; - fetch(API_URL) - .then(r => { if (!r.ok) throw new Error("API 返回错误: " + r.status); return r.json(); }) - .then(data => { + // デフォルト期間を計算(5/31決算:6月開始~現在月末) + function getDefaultPeriod() { + const today = new Date(); + const currentYear = today.getFullYear(); + const currentMonth = today.getMonth() + 1; // 1-12 - console.log("试算表 API 数据:", data); - const tbody = document.querySelector("#trialBalanceTable tbody"); - if (!tbody) { console.error("未找到 #trialBalanceTable tbody"); return; } + let fiscalStartYear, fiscalEndYear; - let totalDebit = 0, totalCredit = 0; - data.accounts.forEach(row => { - const debit = Number(row.debit ?? 0); - const credit = Number(row.credit ?? 0); - totalDebit += debit; totalCredit += credit; + // 6月~5月が1会計年度 + if (currentMonth >= 6) { + // 6月以降:当年6月1日から + fiscalStartYear = currentYear; + fiscalEndYear = currentYear; + } else { + // 1-5月:前年6月1日から + fiscalStartYear = currentYear - 1; + fiscalEndYear = currentYear; + } - const tr = document.createElement("tr"); - tr.innerHTML = ` - ${row.account_code} - ${row.account_name} - ${debit.toLocaleString()} - ${credit.toLocaleString()}`; - tbody.appendChild(tr); + const dateFrom = `${fiscalStartYear}-06-01`; + + // 現在月の末日を計算 + const lastDayOfMonth = new Date(fiscalEndYear, currentMonth, 0).getDate(); + const dateTo = `${fiscalEndYear}-${String(currentMonth).padStart( + 2, + "0" + )}-${String(lastDayOfMonth).padStart(2, "0")}`; + + return { dateFrom, dateTo }; + } + + // 試算表データを読み込む関数 + function loadTrialBalance(dateFrom, dateTo) { + const url = `${API_URL}?date_from=${dateFrom}&date_to=${dateTo}`; + console.log("読み込み URL:", url); + + fetch(url) + .then((r) => { + if (!r.ok) throw new Error("API 返回错误: " + r.status); + return r.json(); + }) + .then((data) => { + console.log("試算表 API データ:", data); + + // 期間情報を更新 + updatePeriodInfo(dateFrom, dateTo); + + const tbody = document.querySelector("#trialBalanceTable tbody"); + if (!tbody) { + console.error("未找到 #trialBalanceTable tbody"); + return; + } + + tbody.innerHTML = ""; + + let totalOpening = 0; + let totalDebit = 0; + let totalCredit = 0; + let totalClosing = 0; + + data.accounts.forEach((row) => { + const debit = Number(row.debit ?? 0); + const credit = Number(row.credit ?? 0); + const opening = Number(row.opening_balance ?? 0); + const closing = Number(row.closing_balance ?? 0); + + // ========================= + // 行タイプ判定 + // ========================= + const isTotalRow = !row.account_code; // null / "" / undefined 全覆盖 + + // 明细行だけ合計に含める + if (!isTotalRow) { + totalOpening += opening; + totalDebit += debit; + totalCredit += credit; + totalClosing += closing; + } + + const tr = document.createElement("tr"); + + if (isTotalRow) { + tr.classList.add("total-row"); + } + + const ratio = row.ratio ?? "0.00"; + + tr.innerHTML = ` + ${row.account_code ?? ""} + ${row.account_name ?? ""} + ${opening ? opening.toLocaleString() : ""} + ${debit ? debit.toLocaleString() : ""} + ${credit ? credit.toLocaleString() : ""} + ${closing ? closing.toLocaleString() : ""} + ${ratio} + `; + + tbody.appendChild(tr); + }); + + // ========================= + // フッター合計 + // ========================= + document.getElementById("totalOpening").textContent = + totalOpening.toLocaleString(); + document.getElementById("totalDebit").textContent = + totalDebit.toLocaleString(); + document.getElementById("totalCredit").textContent = + totalCredit.toLocaleString(); + document.getElementById("totalClosing").textContent = + totalClosing.toLocaleString(); + document.getElementById("totalRatio").textContent = + data.totals?.ratio ?? "100.00"; + }) + .catch((err) => { + console.error("試算表読取失敗:", err); + alert("試算表読取失敗、コンソールログを確認してください"); }); + } - document.getElementById("totalDebit").textContent = totalDebit.toLocaleString(); - document.getElementById("totalCredit").textContent = totalCredit.toLocaleString(); - }) - .catch(err => { console.error("試算表读取失败:", err); alert("試算表读取失败,请查看控制台日志"); }); + // 期間情報を更新する関数 + function updatePeriodInfo(dateFrom, dateTo) { + const fromDate = new Date(dateFrom); + const toDate = new Date(dateTo); + + const fromYear = fromDate.getFullYear() - 2018; // 令和変換(2019年=令和1年) + const toYear = toDate.getFullYear() - 2018; + + const periodText = `令和${fromYear}年 ${ + fromDate.getMonth() + 1 + }月 ${fromDate.getDate()}日 ~ 令和${toYear}年 ${ + toDate.getMonth() + 1 + }月 ${toDate.getDate()}日`; + document.getElementById("periodInfo").textContent = periodText; + } + + // 検索ボタンのイベント + document.getElementById("btnSearch").addEventListener("click", () => { + const dateFrom = document.getElementById("dateFrom").value; + const dateTo = document.getElementById("dateTo").value; + + if (!dateFrom || !dateTo) { + alert("開始年月日と終了年月日を入力してください"); + return; + } + + if (dateFrom > dateTo) { + alert("開始年月日は終了年月日より前の日付を指定してください"); + return; + } + + loadTrialBalance(dateFrom, dateTo); + }); + + // 全年度表示ボタンのイベント + document.getElementById("btnFullYear").addEventListener("click", () => { + const currentYear = new Date().getFullYear(); + const dateFrom = `${currentYear}-01-01`; + const dateTo = `${currentYear}-12-31`; + + document.getElementById("dateFrom").value = dateFrom; + document.getElementById("dateTo").value = dateTo; + + loadTrialBalance(dateFrom, dateTo); + }); + + // 初期読み込み(5/31決算に対応) + const defaultPeriod = getDefaultPeriod(); + document.getElementById("dateFrom").value = defaultPeriod.dateFrom; + document.getElementById("dateTo").value = defaultPeriod.dateTo; + + loadTrialBalance(defaultPeriod.dateFrom, defaultPeriod.dateTo); }); diff --git a/frontend/opening_balance.html b/frontend/opening_balance.html index bc95359..f13279a 100644 --- a/frontend/opening_balance.html +++ b/frontend/opening_balance.html @@ -39,10 +39,9 @@
- ← 戻る + -s diff --git a/frontend/trial-balance.html b/frontend/trial-balance.html index b43302b..b6f403f 100644 --- a/frontend/trial-balance.html +++ b/frontend/trial-balance.html @@ -5,33 +5,109 @@ 試算表 -

試算表

+

残高試算表(貸借・損益)

+ +
+ + + + + + + + +
+ +
+ 令和7年 1月 1日 ~ 令和7年 12月31日 + 単位: 円 +
@@ -39,9 +115,10 @@ - - + + + @@ -54,54 +131,14 @@ +
科目コード 科目名称 期首残高借方合計贷方合計借方発生額貸方発生額 期末残高構成比(%)
0 0 0100.00
- + + diff --git a/reports/pdf/★残高試算表_20250724.pdf b/reports/pdf/★残高試算表_20250724.pdf new file mode 100644 index 0000000..14b9486 Binary files /dev/null and b/reports/pdf/★残高試算表_20250724.pdf differ