修正
This commit is contained in:
@@ -4,10 +4,20 @@ async function apiGet(path, params = {}) {
|
||||
const query = new URLSearchParams(params).toString();
|
||||
const url = `${API_BASE}${path}?${query}`;
|
||||
|
||||
console.log(`📡 API GET: ${url}`);
|
||||
|
||||
const res = await fetch(url);
|
||||
|
||||
console.log(`📊 Response status: ${res.status}`);
|
||||
|
||||
if (!res.ok) {
|
||||
const data = await res.json();
|
||||
console.error(`❌ API Error:`, data);
|
||||
throw new Error(data.detail || "APIエラー");
|
||||
}
|
||||
return res.json();
|
||||
|
||||
const jsonData = await res.json();
|
||||
console.log(`✅ API Response:`, jsonData);
|
||||
|
||||
return jsonData;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
let accounts = [];
|
||||
let retainedEarnings = null;
|
||||
let isLocked = false;
|
||||
let isDataLoaded = false; // ★ データ読込済みフラグ(保存時の検証用)
|
||||
|
||||
// 科目グループ定義(小計付き)
|
||||
// 科目グループ定義(小計付き)- 試算表と同じ順序
|
||||
const GROUPS = [
|
||||
{
|
||||
key: "asset",
|
||||
label: "資産",
|
||||
subGroups: [
|
||||
{ label: "現金・預金合計", codes: ["101", "102", "103", "104"] },
|
||||
{ label: "売上債権合計", codes: ["201"] },
|
||||
{ label: "現金・預金合計", codes: ["101", "102", "103", "104", "105"] },
|
||||
{ label: "売上債権合計", codes: ["201", "202"] },
|
||||
{ label: "有価証券合計", codes: ["301"] },
|
||||
{ label: "棚卸資産合計", codes: ["210"] },
|
||||
{
|
||||
label: "他流動資産合計",
|
||||
codes: ["203", "204", "205", "206", "207", "208"],
|
||||
},
|
||||
{
|
||||
label: "流動資産合計",
|
||||
codes: [
|
||||
@@ -17,14 +24,25 @@ const GROUPS = [
|
||||
"102",
|
||||
"103",
|
||||
"104",
|
||||
"105",
|
||||
"201",
|
||||
"202",
|
||||
"203",
|
||||
"204",
|
||||
"205",
|
||||
"206",
|
||||
"207",
|
||||
"208",
|
||||
"210",
|
||||
"301",
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "有形固定資産合計",
|
||||
codes: ["401", "402", "403", "404", "405", "406", "407"],
|
||||
},
|
||||
{ label: "無形固定資産合計", codes: ["408"] },
|
||||
{ label: "投資その他の資産合計", codes: ["301", "410"] },
|
||||
{
|
||||
label: "固定資産合計",
|
||||
codes: [
|
||||
@@ -40,6 +58,35 @@ const GROUPS = [
|
||||
"410",
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "資産合計",
|
||||
codes: [
|
||||
"101",
|
||||
"102",
|
||||
"103",
|
||||
"104",
|
||||
"105",
|
||||
"201",
|
||||
"202",
|
||||
"203",
|
||||
"204",
|
||||
"205",
|
||||
"206",
|
||||
"207",
|
||||
"208",
|
||||
"210",
|
||||
"301",
|
||||
"401",
|
||||
"402",
|
||||
"403",
|
||||
"404",
|
||||
"405",
|
||||
"406",
|
||||
"407",
|
||||
"408",
|
||||
"410",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -52,13 +99,17 @@ const GROUPS = [
|
||||
codes: ["501", "502", "503", "504", "505", "506", "507", "508"],
|
||||
},
|
||||
{ label: "固定負債合計", codes: ["509"] },
|
||||
{
|
||||
label: "負債合計",
|
||||
codes: ["501", "502", "503", "504", "505", "506", "507", "508", "509"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "equity",
|
||||
label: "純資産",
|
||||
subGroups: [
|
||||
{ label: "株主資本合計", codes: ["601", "602"] },
|
||||
{ label: "資本金合計", codes: ["601"] },
|
||||
{ label: "純資産合計", codes: ["601", "602"] },
|
||||
],
|
||||
},
|
||||
@@ -163,102 +214,67 @@ function updateSubtotalRows() {
|
||||
async function loadOpeningBalances() {
|
||||
const year = document.getElementById("fiscalYear").value;
|
||||
|
||||
const data = await apiGet("/opening-balances", { fiscal_year: year });
|
||||
console.log(`🔄 Loading opening balances for year: ${year}`);
|
||||
|
||||
accounts = data;
|
||||
retainedEarnings = accounts.find(
|
||||
(a) => a.account_name === "繰越利益剰余金" || a.account_name === "繰越利益",
|
||||
);
|
||||
try {
|
||||
const data = await apiGet("/opening-balances", { fiscal_year: year });
|
||||
|
||||
const tbody = document.querySelector("#balanceTable tbody");
|
||||
tbody.innerHTML = "";
|
||||
console.log(`✓ Received data:`, data);
|
||||
console.log(`✓ Total accounts: ${data.length}`);
|
||||
|
||||
GROUPS.forEach((group) => {
|
||||
// 分组标题行
|
||||
const headerTr = document.createElement("tr");
|
||||
headerTr.innerHTML = `
|
||||
<td colspan="4" style="background:#ddd; font-weight:bold;">
|
||||
${group.label}
|
||||
</td>
|
||||
`;
|
||||
tbody.appendChild(headerTr);
|
||||
accounts = data;
|
||||
retainedEarnings = accounts.find(
|
||||
(a) =>
|
||||
a.account_name === "繰越利益剰余金" || a.account_name === "繰越利益",
|
||||
);
|
||||
|
||||
const groupAccounts = accounts.filter((a) => a.account_type === group.key);
|
||||
const tbody = document.querySelector("#balanceTable tbody");
|
||||
console.log(`📌 tbody element:`, tbody);
|
||||
|
||||
groupAccounts.forEach((a, idx) => {
|
||||
const i = accounts.indexOf(a);
|
||||
tbody.innerHTML = "";
|
||||
|
||||
// シンプルに全科目を表示(グループ分けは後で)
|
||||
console.log(`🔄 Starting to render ${data.length} accounts...`);
|
||||
|
||||
accounts.forEach((a, idx) => {
|
||||
const isRetained =
|
||||
a.account_name === "繰越利益剰余金" || a.account_name === "繰越利益";
|
||||
|
||||
const tr = document.createElement("tr");
|
||||
|
||||
tr.innerHTML = `
|
||||
<td>${a.account_code}</td>
|
||||
<td>${a.account_name}</td>
|
||||
<td>
|
||||
<input type="text" value="${formatNumber(a.opening_debit)}"
|
||||
${isRetained ? "readonly" : ""}
|
||||
onchange="accounts[${i}].opening_debit=parseNumber(this.value); this.value=formatNumber(accounts[${i}].opening_debit); recalcTotals();">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" value="${formatNumber(a.opening_credit)}"
|
||||
${isRetained ? "readonly" : ""}
|
||||
onchange="accounts[${i}].opening_credit=parseNumber(this.value); this.value=formatNumber(accounts[${i}].opening_credit); recalcTotals();">
|
||||
</td>
|
||||
`;
|
||||
|
||||
<td>${a.account_code}</td>
|
||||
<td>${a.account_name}</td>
|
||||
<td>
|
||||
<input type="text" value="${formatNumber(a.opening_debit)}"
|
||||
${isRetained ? "readonly" : ""}
|
||||
onchange="accounts[${idx}].opening_debit=parseNumber(this.value); this.value=formatNumber(accounts[${idx}].opening_debit); recalcTotals();">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" value="${formatNumber(a.opening_credit)}"
|
||||
${isRetained ? "readonly" : ""}
|
||||
onchange="accounts[${idx}].opening_credit=parseNumber(this.value); this.value=formatNumber(accounts[${idx}].opening_credit); recalcTotals();">
|
||||
</td>
|
||||
`;
|
||||
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 = `
|
||||
<td></td>
|
||||
<td>${subGroup.label}</td>
|
||||
<td>${formatNumber(subGroupTotal.debit)}</td>
|
||||
<td>${formatNumber(subGroupTotal.credit)}</td>
|
||||
`;
|
||||
tbody.appendChild(totalTr);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 年度锁定チェック
|
||||
fetch(`/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();
|
||||
console.log(`✅ Table rendered!`, tbody.innerHTML.substring(0, 100));
|
||||
|
||||
isDataLoaded = true; // ★ データ読込済みフラグを設定
|
||||
recalcTotals();
|
||||
} catch (e) {
|
||||
console.error("❌ Error loading opening balances:", e);
|
||||
alert(`読込エラー: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function saveOpeningBalances() {
|
||||
// ★ 重要:データを読込まずに保存するのを防止
|
||||
if (!isDataLoaded) {
|
||||
alert("先に「読込」ボタンで期首残高を読み込んでください。");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isLocked) {
|
||||
alert("この会計年度の期首残高は確定されているため、保存できません。");
|
||||
return;
|
||||
@@ -279,6 +295,12 @@ async function saveOpeningBalances() {
|
||||
credit: Number(a.opening_credit || 0),
|
||||
}));
|
||||
|
||||
// ★ 重要:データが全て0の場合、保存を拒否
|
||||
if (balances.length === 0) {
|
||||
alert("全科目の残高が0です。期首残高データを入力してください。");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await fetch(`/opening-balances`, {
|
||||
method: "POST",
|
||||
@@ -295,6 +317,8 @@ async function saveOpeningBalances() {
|
||||
});
|
||||
|
||||
alert("期首残高を保存しました");
|
||||
// メインメニューに移動
|
||||
location.href = "index.html";
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
|
||||
@@ -99,6 +99,11 @@ function initTrialBalance() {
|
||||
tr.classList.add("total-row");
|
||||
}
|
||||
|
||||
// indent フラグに基づくクラス追加
|
||||
if (row.indent) {
|
||||
tr.classList.add("indent-row");
|
||||
}
|
||||
|
||||
const ratio = row.ratio ?? "0.00";
|
||||
|
||||
tr.innerHTML = `
|
||||
|
||||
Reference in New Issue
Block a user