let accounts = []; let retainedEarnings = null; let isLocked = false; function formatNumber(n) { return Number(n || 0).toLocaleString(); } function recalcTotals() { let debit = 0; let credit = 0; // 先算:不含繰越利益 accounts.forEach((a) => { if (a.account_name === "繰越利益") return; debit += Number(a.opening_debit || 0); credit += Number(a.opening_credit || 0); }); const diff = debit - credit; // 自动计算 繰越利益 if (retainedEarnings) { if (diff > 0) { retainedEarnings.opening_debit = 0; retainedEarnings.opening_credit = diff; } else { retainedEarnings.opening_debit = -diff; retainedEarnings.opening_credit = 0; } } // 再算一次(包含繰越利益) let finalDebit = 0; let finalCredit = 0; accounts.forEach((a) => { finalDebit += Number(a.opening_debit || 0); finalCredit += Number(a.opening_credit || 0); }); document.getElementById("totalDebit").innerText = formatNumber(finalDebit); document.getElementById("totalCredit").innerText = formatNumber(finalCredit); document.getElementById("diff").innerText = formatNumber( finalDebit - finalCredit ); } async function loadOpeningBalances() { const year = document.getElementById("fiscalYear").value; const data = await apiGet("/opening-balances", { fiscal_year: year }); accounts = data; retainedEarnings = accounts.find((a) => a.account_name === "繰越利益"); const tbody = document.querySelector("#balanceTable tbody"); tbody.innerHTML = ""; GROUPS.forEach((group) => { // 分组标题行 const headerTr = document.createElement("tr"); headerTr.innerHTML = ` ${group.label} `; tbody.appendChild(headerTr); accounts.forEach((a, i) => { if (a.account_type !== group.key) return; const isRetained = a.account_name === "繰越利益"; const tr = document.createElement("tr"); tr.innerHTML = ` ${a.account_code} ${a.account_name} `; tbody.appendChild(tr); }); }); // 年度锁定チェック fetch( `http://127.0.0.1:18080/opening-balances/lock-status?fiscal_year=${year}` ) .then((res) => res.json()) .then((data) => { isLocked = data.is_locked; document.querySelector( "button[onclick='saveOpeningBalances()']" ).disabled = isLocked; if (isLocked) { alert("この会計年度の期首残高は確定されています。"); } }); recalcTotals(); } async function saveOpeningBalances() { if (isLocked) { alert("この会計年度の期首残高は確定されているため、保存できません。"); return; } const year = Number(document.getElementById("fiscalYear").value); const balances = accounts // ❗ 排除 繰越利益 .filter( (a) => a.account_name !== "繰越利益" && (Number(a.opening_debit) !== 0 || Number(a.opening_credit) !== 0) ) .map((a) => ({ account_id: a.account_id, debit: Number(a.opening_debit || 0), credit: Number(a.opening_credit || 0), })); try { await fetch("http://127.0.0.1:18080/opening-balances", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ fiscal_year: year, balances: balances, }), }).then(async (res) => { if (!res.ok) { const data = await res.json(); throw new Error(data.detail); } }); alert("期首残高を保存しました"); } catch (e) { alert(e.message); } }