// =============================================== // trial-balance.js - 試算表データ読み込みスクリプト // =============================================== console.log("🔧 trial-balance.js ファイルが読み込まれました"); // 防止重复初始化 let trialBalanceInitialized = false; // 初期読込関数 function initTrialBalance() { // 已经初始化过就直接返回 if (trialBalanceInitialized) { console.log("⚠️ initTrialBalance() はすでに実行済みです"); return; } trialBalanceInitialized = true; console.log("✓ initTrialBalance() 関数が実行されました"); const API_URL = `/trial-balance`; // デフォルト期間を計算(5/31決算:6月開始~現在月末) function getDefaultPeriod() { const today = new Date(); const currentYear = today.getFullYear(); const currentMonth = today.getMonth() + 1; // 1-12 let fiscalStartYear, fiscalEndYear; // 6月~5月が1会計年度 if (currentMonth >= 6) { // 6月以降:当年6月1日から fiscalStartYear = currentYear; fiscalEndYear = currentYear; } else { // 1-5月:前年6月1日から fiscalStartYear = currentYear - 1; fiscalEndYear = currentYear; } 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); // 加載中のステータス表示 const loadingEl = document.getElementById("loadingStatus"); if (loadingEl) loadingEl.style.display = "inline"; fetch(url) .then((r) => { console.log("API Response Status:", r.status); 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 = ""; // ========================================= // 科目タイプ判定関数(日本企業損益計算書対応) // ========================================= function getAccountType(accountCode) { if (!accountCode) return "unknown"; const code = parseInt(accountCode); // 営業収益(売上高) if (code === 701) return "operating_revenue"; // 非営業収益(受取利息、雑収入) if (code === 702 || code === 709) return "non_operating_revenue"; // 売上原価(仕入高) if (code === 801) return "cogs"; // 営業費用(仕入高以外の800-899、ただし520-599は除外) if (code >= 802 && code <= 899) return "operating_expense"; // 法人税等(504)- 損益計算に含めない if (code === 504) return "corporate_tax"; // 消費税等(505)- 損益計算に含めない if (code === 505) return "consumption_tax"; // その他は貸借科目 return "balance_sheet"; } // ========================================= // 各タイプの合計集計(日本企業損益計算書対応) // ========================================= let operatingRevenue = 0; // 701: 売上高(貸方) let cogs = 0; // 801: 売上原価(借方) let operatingExpenses = 0; // 802-820: 営業費用(借方) let nonOperatingRevenue = 0; // 702, 709: 営業外収益(貸方) let corporateTax = 0; // 504: 法人税等(貸方) let consumptionTax = 0; // 505: 消費税(貸方) // 税理士資料形式:P/L科目の原始借方・貸方合計 let plTotalDebit = 0; // 全費用科目の借方合計(801-820) let plTotalCredit = 0; // 全収益科目の貸方合計(701-709) let bsDebit = 0; // 貸借科目合計(借方) let bsCredit = 0; // 貸借科目合計(貸方) data.accounts.forEach((row) => { const code = row.account_code; const debit = Number(row.debit ?? 0); const credit = Number(row.credit ?? 0); const type = getAccountType(code); if (type === "operating_revenue") { operatingRevenue += credit - debit; plTotalCredit += credit; } else if (type === "non_operating_revenue") { nonOperatingRevenue += credit - debit; plTotalCredit += credit; } else if (type === "cogs") { cogs += debit - credit; plTotalDebit += debit; } else if (type === "operating_expense") { operatingExpenses += debit - credit; plTotalDebit += debit; } else if (type === "balance_sheet") { bsDebit += debit; bsCredit += credit; } }); // 日本企業損益計算書の標準フロー // ※ 504(未払法人税等)、505(未払消費税等) は負債科目のため損益計算に含めない const grossProfit = operatingRevenue - cogs; // 売上総利益 = 売上高 - 売上原価 const operatingProfit = grossProfit - operatingExpenses; // 営業利益 = 売上総利益 - 営業費用 const ordinaryProfit = operatingProfit + nonOperatingRevenue; // 経常利益 = 営業利益 + 営業外収益 // 801-820科目の合計を計算用の変量 let operatingExpense801_820Debit = 0; let operatingExpense801_820Credit = 0; // 税引前当期純利益 = 経常利益(特別損益の分類がないため) const netProfit = ordinaryProfit; // 当期純利益 = 経常利益(法人税等は損益計算に含めない) data.accounts.forEach((row) => { const code = row.account_code; 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 全覆盖 const tr = document.createElement("tr"); if (isTotalRow) { tr.classList.add("total-row"); } // indent フラグに基づくクラス追加 if (row.indent) { tr.classList.add("indent-row"); } const ratio = row.ratio ?? "0.00"; // ========================= // 借方・貸方は全科目共通でそのまま表示 // ========================= const displayDebit = debit ? debit.toLocaleString() : ""; const displayCredit = credit ? credit.toLocaleString() : ""; // 期末残高のフォーマット(マイナス値も表示) let displayClosing = ""; if (closing !== 0) { displayClosing = closing.toLocaleString(); } tr.innerHTML = ` ${row.account_code ?? ""} ${row.account_name ?? ""} ${opening ? opening.toLocaleString() : ""} ${displayDebit} ${displayCredit} ${displayClosing} ${ratio} `; tbody.appendChild(tr); }); // フッター合計は削除 // 加載中ステータスを非表示 const loadingEl = document.getElementById("loadingStatus"); if (loadingEl) loadingEl.style.display = "none"; }) .catch((err) => { console.error("試算表読取失敗:", err); const tbody = document.querySelector("#trialBalanceTable tbody"); if (tbody) { tbody.innerHTML = `エラー: ${err.message}`; } // 加載中ステータスを非表示 const loadingEl = document.getElementById("loadingStatus"); if (loadingEl) loadingEl.style.display = "none"; alert( `試算表読取失敗: ${err.message}\n\nコンソールログ(F12)を確認してください`, ); }); } // 期間情報を更新する関数 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; // thead内の期間情報も更新(印刷用) const theadPeriodInfo = document.getElementById("theadPeriodInfo"); if (theadPeriodInfo) { theadPeriodInfo.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); }); // 印刷ボタンのイベント(最新データを読み込んでから印刷) document.getElementById("btnPrint").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); // データ読込完了を待って印刷開始(少し遅延させる) setTimeout(() => { setupPrintDate(); // 打印前更新日期 window.print(); }, 800); }); // 初期読み込み(5/31決算に対応) try { const defaultPeriod = getDefaultPeriod(); document.getElementById("dateFrom").value = defaultPeriod.dateFrom; document.getElementById("dateTo").value = defaultPeriod.dateTo; loadTrialBalance(defaultPeriod.dateFrom, defaultPeriod.dateTo); console.log("✓ 初期読み込み完了"); } catch (err) { console.error("初期読み込みエラー:", err); } } // 複数の方式で初期化を実行(iOS互換性を向上) document.addEventListener("DOMContentLoaded", initTrialBalance); window.addEventListener("load", initTrialBalance); if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initTrialBalance); } else { // ページがすでに読み込まれている場合、すぐに実行 setTimeout(initTrialBalance, 100); } // 打印日期和页码设置 // =============================================== function setupPrintDate() { const printDateEl = document.getElementById("printDate"); if (!printDateEl) return; // 设置打印日期 const today = new Date(); const year = today.getFullYear(); const month = String(today.getMonth() + 1).padStart(2, "0"); const date = String(today.getDate()).padStart(2, "0"); const dateStr = `${year}年${month.replace(/^0/, "")}月${date.replace( /^0/, "", )}日`; printDateEl.textContent = `印刷日:${dateStr}`; // thead内の印刷日も更新 const theadPrintDate = document.getElementById("theadPrintDate"); if (theadPrintDate) { theadPrintDate.textContent = `印刷日:${dateStr}`; console.log("✓ 印刷日を更新しました:", theadPrintDate.textContent); } else { console.warn("⚠️ theadPrintDate要素が見つかりません"); } } // 页面加载时设置一次(仅一次) if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", setupPrintDate, { once: true }); } else { setupPrintDate(); }