Files
njts-accounting-core/frontend/js/trial-balance.js
2026-03-03 00:01:43 +09:00

368 lines
13 KiB
JavaScript
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.
// ===============================================
// 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 = `
<td class="left">${row.account_code ?? ""}</td>
<td class="left">${row.account_name ?? ""}</td>
<td class="right">${opening ? opening.toLocaleString() : ""}</td>
<td class="right">${displayDebit}</td>
<td class="right">${displayCredit}</td>
<td class="right">${displayClosing}</td>
<td class="right">${ratio}</td>
`;
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 = `<tr><td colspan="7" style="text-align:center; color:red;">エラー: ${err.message}</td></tr>`;
}
// 加載中ステータスを非表示
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();
}