This commit is contained in:
admin
2026-01-13 16:29:34 +09:00
parent 59e8e8625d
commit c753e89f49
11 changed files with 1046 additions and 121 deletions

View File

@@ -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 = `
<td>${a.account_code}</td>
<td>${a.account_name}</td>
<td>
<input type="number" value="${a.opening_debit}"
<input type="text" value="${formatNumber(a.opening_debit)}"
${isRetained ? "readonly" : ""}
onchange="accounts[${i}].opening_debit=this.value; recalcTotals();">
onchange="accounts[${i}].opening_debit=parseNumber(this.value); this.value=formatNumber(accounts[${i}].opening_debit); recalcTotals();">
</td>
<td>
<input type="number" value="${a.opening_credit}"
<input type="text" value="${formatNumber(a.opening_credit)}"
${isRetained ? "readonly" : ""}
onchange="accounts[${i}].opening_credit=this.value; recalcTotals();">
onchange="accounts[${i}].opening_credit=parseNumber(this.value); this.value=formatNumber(accounts[${i}].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);
}
});
});
});
@@ -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)
)