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

@@ -52,7 +52,13 @@
<div class="row">
<label>仕訳日</label>
<input type="date" id="entryDate" onchange="onEntryDateChanged()" />
<input
type="date"
id="entryDate"
min="1900-01-01"
max="2999-12-31"
onchange="onEntryDateChanged()"
/>
</div>
<div class="row">
@@ -60,9 +66,11 @@
<input
type="text"
id="description"
style="flex: 1"
list="descriptionHistory"
style="flex: 3; min-width: 600px"
placeholder="例:売上入金/経費支払 など"
/>
<datalist id="descriptionHistory"></datalist>
</div>
<div class="row">
@@ -100,6 +108,8 @@
<button onclick="submitJournal()">登録</button>
</div>
<hr style="margin: 40px 0; border: none; border-top: 2px solid #ddd" />
<h3>月次锁定管理</h3>
<div style="margin-bottom: 8px">
@@ -148,6 +158,43 @@
🔒 该月份已锁定,不能编辑仕訳
</div>
<!-- ========================================
仕訳検索セクションjournal-list.htmlから統合
======================================== -->
<hr style="margin: 40px 0; border: none; border-top: 2px solid #ddd" />
<h2>仕訳検索</h2>
<div style="margin: 16px 0">
<label>期間</label>
<input
type="date"
id="searchFromDate"
min="1900-01-01"
max="2999-12-31"
/>
<input type="date" id="searchToDate" min="1900-01-01" max="2999-12-31" />
<label style="margin-left: 16px">摘要</label>
<input type="text" id="searchKeyword" />
<button onclick="searchJournals()" style="margin-left: 8px">検索</button>
</div>
<table>
<thead>
<tr>
<th>日付</th>
<th>摘要</th>
<th>借方合計</th>
<th>貸方合計</th>
<th>操作</th>
</tr>
</thead>
<tbody id="searchResultList"></tbody>
</table>
<script>
let rowSeq = 0; // 各行唯一ID
@@ -229,10 +276,50 @@
.slice(0, 10);
await onEntryDateChanged();
// 摘要履歴を読み込み
loadDescriptionHistory();
}
init();
// -----------------------------
// 摘要履歴管理
// -----------------------------
function loadDescriptionHistory() {
const history = JSON.parse(
localStorage.getItem("descriptionHistory") || "[]"
);
const datalist = document.getElementById("descriptionHistory");
datalist.innerHTML = "";
history.forEach((desc) => {
const option = document.createElement("option");
option.value = desc;
datalist.appendChild(option);
});
}
function saveDescriptionToHistory(description) {
if (!description || description.trim() === "") return;
let history = JSON.parse(
localStorage.getItem("descriptionHistory") || "[]"
);
// 重複を避け、先頭に追加
history = history.filter((d) => d !== description);
history.unshift(description);
// 最大20件まで保存
if (history.length > 20) {
history = history.slice(0, 20);
}
localStorage.setItem("descriptionHistory", JSON.stringify(history));
loadDescriptionHistory();
}
// -----------------------------
// 仮払/仮受 セレクタ
// -----------------------------
@@ -490,6 +577,10 @@
}
const result = await res.json();
// 摘要を履歴に保存
saveDescriptionToHistory(description);
alert(`登録完了ID=${result.journal_entry_id}`);
location.reload();
}
@@ -664,6 +755,256 @@
return html;
}
// ========================================
// 仕訳検索機能journal-list.htmlから統合
// ========================================
async function searchJournals() {
const from = document.getElementById("searchFromDate").value;
const to = document.getElementById("searchToDate").value;
const key = document.getElementById("searchKeyword").value;
// 検索条件を保存
localStorage.setItem(
"journalSearchConditions",
JSON.stringify({
fromDate: from,
toDate: to,
keyword: key,
})
);
const qs = new URLSearchParams();
if (from) qs.append("from_date", from);
if (to) qs.append("to_date", to);
if (key) qs.append("keyword", key);
const res = await fetch(`${API}/journal-entries?${qs.toString()}`);
const data = await res.json();
const tbody = document.getElementById("searchResultList");
tbody.innerHTML = "";
data.forEach((e) => {
const tr = document.createElement("tr");
tr.innerHTML = `
<td>${e.entry_date}</td>
<td>${e.description}</td>
<td style="text-align:right">${e.debit_total.toLocaleString()}</td>
<td style="text-align:right">${e.credit_total.toLocaleString()}</td>
<td>
<button onclick="viewJournal(${e.journal_entry_id})">表示</button>
<button onclick="copyJournal(${
e.journal_entry_id
})" style="color: green;">複製</button>
<button onclick="reverseAndEdit(${e.journal_entry_id})">修正</button>
<button onclick="deleteJournalEntry(${
e.journal_entry_id
})" style="color: red;">削除</button>
</td>
`;
tbody.appendChild(tr);
});
}
function viewJournal(id) {
window.open(`journal-view.html?id=${id}`, "_blank");
}
// --------------------------------------------------
// 仕訳複製:既存の仕訳を入力フォームにコピー
// --------------------------------------------------
async function copyJournal(id) {
try {
const res = await fetch(`${API}/journal-entries/${id}`);
if (!res.ok) {
alert("仕訳の取得に失敗しました");
return;
}
const data = await res.json();
// 日付は今日の日付をデフォルトに(必要に応じて変更可能)
document.getElementById("entryDate").value = new Date()
.toISOString()
.slice(0, 10);
// 摘要をコピー
document.getElementById("description").value = data.description || "";
// 既存の行をすべてクリア(税行も含む)
const tbody = document.querySelector("#linesTable tbody");
tbody.innerHTML = "";
rowSeq = 0;
// 仕訳明細をコピー
if (data.lines && data.lines.length > 0) {
data.lines.forEach((line) => {
const tr = addRow();
// 科目を設定
const accountSelect = tr.querySelector(".account");
if (accountSelect) {
accountSelect.value = line.account_id;
}
// 借方・貸方金額を設定
const debitInput = tr.querySelector(".debit");
const creditInput = tr.querySelector(".credit");
if (debitInput && line.debit) {
debitInput.value = line.debit;
}
if (creditInput && line.credit) {
creditInput.value = line.credit;
}
// 税区分を設定
const taxTypeSelect = tr.querySelector(".taxType");
const taxDirSelect = tr.querySelector(".taxDirection");
if (line.tax_rate && taxTypeSelect) {
taxTypeSelect.value = String(line.tax_rate);
}
if (line.tax_direction && taxDirSelect) {
taxDirSelect.value = line.tax_direction;
}
});
}
// ページ上部の仕訳入力フォームにスクロール
window.scrollTo({ top: 0, behavior: "smooth" });
alert(
"仕訳を複製しました。日付や金額などを修正して登録してください。"
);
} catch (error) {
console.error("Error:", error);
alert(`エラーが発生しました: ${error.message}`);
}
}
// --------------------------------------------------
// 修正仕訳:逆仕訳を作って修正画面へ遷移
// --------------------------------------------------
async function reverseAndEdit(id) {
if (
!confirm(
"この仕訳の逆仕訳を作成し、修正仕訳入力画面を開きます。よろしいですか?"
)
)
return;
try {
// ① 逆仕訳を作成
const res = await fetch(`${API}/journal-entries/${id}/reverse`, {
method: "POST",
});
if (!res.ok) {
const data = await res.json().catch(() => ({}));
alert(
data.detail || `逆仕訳の作成に失敗しました (HTTP ${res.status})`
);
return;
}
const data = await res.json();
alert(
`逆仕訳を作成しましたID: ${data.reversed_journal_entry_id}`
);
// ② 元仕訳内容を取得して修正画面へ遷移
const srcRes = await fetch(`${API}/journal-entries/${id}`);
if (!srcRes.ok) {
alert("元仕訳の取得に失敗しました");
return;
}
const srcData = await srcRes.json();
localStorage.setItem("editSource", JSON.stringify(srcData));
// 修正画面を開く
window.open("journal-edit.html", "_blank");
} catch (error) {
console.error("Error:", error);
alert(`エラーが発生しました: ${error.message}`);
}
}
// --------------------------------------------------
// 仕訳削除
// --------------------------------------------------
async function deleteJournalEntry(id) {
const reason = prompt("削除理由を入力してください:");
if (!reason || reason.trim() === "") {
alert("削除理由の入力が必要です");
return;
}
if (!confirm("この仕訳を削除してもよろしいですか?")) {
return;
}
const res = await fetch(`${API}/journal-entries/${id}`, {
method: "DELETE",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ deleted_reason: reason }),
});
const data = await res.json();
if (!res.ok) {
alert(data.detail || "削除に失敗しました");
return;
}
alert("削除しました");
searchJournals(); // リストを再読み込み
}
// ページロード後に前回の検索条件を復元
window.addEventListener("DOMContentLoaded", () => {
// デフォルト期間を設定5/31決算6月1日現在
const today = new Date();
const currentYear = today.getFullYear();
const currentMonth = today.getMonth() + 1; // 1-12
let fiscalStartYear;
if (currentMonth >= 6) {
// 6月以降当年6月1日から
fiscalStartYear = currentYear;
} else {
// 1-5月前年6月1日から
fiscalStartYear = currentYear - 1;
}
const defaultFromDate = `${fiscalStartYear}-06-01`;
const defaultToDate = today.toISOString().slice(0, 10);
const savedConditions = localStorage.getItem("journalSearchConditions");
if (savedConditions) {
const conditions = JSON.parse(savedConditions);
if (conditions.fromDate)
document.getElementById("searchFromDate").value =
conditions.fromDate;
else
document.getElementById("searchFromDate").value = defaultFromDate;
if (conditions.toDate)
document.getElementById("searchToDate").value = conditions.toDate;
else document.getElementById("searchToDate").value = defaultToDate;
if (conditions.keyword)
document.getElementById("searchKeyword").value = conditions.keyword;
// 自動的に検索を実行
searchJournals();
} else {
// 保存された条件がない場合はデフォルト値を設定
document.getElementById("searchFromDate").value = defaultFromDate;
document.getElementById("searchToDate").value = defaultToDate;
}
});
</script>
</body>
</html>

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)
)

View File

@@ -1,32 +1,178 @@
document.addEventListener("DOMContentLoaded", () => {
console.log("trial-balance.js 已执行");
const API_URL = "http://127.0.0.1:18080/trial-balance";
fetch(API_URL)
.then(r => { if (!r.ok) throw new Error("API 返回错误: " + r.status); return r.json(); })
.then(data => {
// デフォルト期間を計算5/31決算6月開始現在月末
function getDefaultPeriod() {
const today = new Date();
const currentYear = today.getFullYear();
const currentMonth = today.getMonth() + 1; // 1-12
console.log("试算表 API 数据:", data);
const tbody = document.querySelector("#trialBalanceTable tbody");
if (!tbody) { console.error("未找到 #trialBalanceTable tbody"); return; }
let fiscalStartYear, fiscalEndYear;
let totalDebit = 0, totalCredit = 0;
data.accounts.forEach(row => {
const debit = Number(row.debit ?? 0);
const credit = Number(row.credit ?? 0);
totalDebit += debit; totalCredit += credit;
// 6月5月が1会計年度
if (currentMonth >= 6) {
// 6月以降当年6月1日から
fiscalStartYear = currentYear;
fiscalEndYear = currentYear;
} else {
// 1-5月前年6月1日から
fiscalStartYear = currentYear - 1;
fiscalEndYear = currentYear;
}
const tr = document.createElement("tr");
tr.innerHTML = `
<td class="left">${row.account_code}</td>
<td class="left">${row.account_name}</td>
<td>${debit.toLocaleString()}</td>
<td>${credit.toLocaleString()}</td>`;
tbody.appendChild(tr);
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);
fetch(url)
.then((r) => {
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 = "";
let totalOpening = 0;
let totalDebit = 0;
let totalCredit = 0;
let totalClosing = 0;
data.accounts.forEach((row) => {
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 全覆盖
// 明细行だけ合計に含める
if (!isTotalRow) {
totalOpening += opening;
totalDebit += debit;
totalCredit += credit;
totalClosing += closing;
}
const tr = document.createElement("tr");
if (isTotalRow) {
tr.classList.add("total-row");
}
const ratio = row.ratio ?? "0.00";
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">${debit ? debit.toLocaleString() : ""}</td>
<td class="right">${credit ? credit.toLocaleString() : ""}</td>
<td class="right">${closing ? closing.toLocaleString() : ""}</td>
<td class="right">${ratio}</td>
`;
tbody.appendChild(tr);
});
// =========================
// フッター合計
// =========================
document.getElementById("totalOpening").textContent =
totalOpening.toLocaleString();
document.getElementById("totalDebit").textContent =
totalDebit.toLocaleString();
document.getElementById("totalCredit").textContent =
totalCredit.toLocaleString();
document.getElementById("totalClosing").textContent =
totalClosing.toLocaleString();
document.getElementById("totalRatio").textContent =
data.totals?.ratio ?? "100.00";
})
.catch((err) => {
console.error("試算表読取失敗:", err);
alert("試算表読取失敗、コンソールログを確認してください");
});
}
document.getElementById("totalDebit").textContent = totalDebit.toLocaleString();
document.getElementById("totalCredit").textContent = totalCredit.toLocaleString();
})
.catch(err => { console.error("試算表读取失败:", err); alert("試算表读取失败,请查看控制台日志"); });
// 期間情報を更新する関数
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;
}
// 検索ボタンのイベント
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);
});
// 初期読み込み5/31決算に対応
const defaultPeriod = getDefaultPeriod();
document.getElementById("dateFrom").value = defaultPeriod.dateFrom;
document.getElementById("dateTo").value = defaultPeriod.dateTo;
loadTrialBalance(defaultPeriod.dateFrom, defaultPeriod.dateTo);
});

View File

@@ -39,10 +39,9 @@
<br />
<button onclick="saveOpeningBalances()">保存</button>
<a href="index.html">← 戻る</a>
<button onclick="location.href='trial-balance.html'">← 戻る</button>
<script src="js/api.js"></script>
<script src="js/opening_balance.js"></script>
</body>
</html>
s

View File

@@ -5,33 +5,109 @@
<title>試算表</title>
<style>
body {
font-family: sans-serif;
font-family: 'MS Gothic', 'Meiryo', sans-serif;
margin: 24px;
font-size: 13px;
}
h2 {
text-align: center;
margin-bottom: 20px;
}
.search-form {
text-align: center;
margin-bottom: 15px;
padding: 15px;
background: #f5f5f5;
border: 1px solid #ddd;
border-radius: 4px;
}
.search-form label {
margin-right: 5px;
font-weight: bold;
}
.search-form input {
margin-right: 15px;
padding: 5px 10px;
border: 1px solid #999;
border-radius: 3px;
font-size: 13px;
}
.search-form button {
padding: 6px 20px;
margin: 0 5px;
background: #4CAF50;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 13px;
}
.search-form button:hover {
background: #45a049;
}
.search-form button.secondary {
background: #2196F3;
}
.search-form button.secondary:hover {
background: #0b7dda;
}
.period-info {
text-align: center;
margin-bottom: 10px;
font-size: 12px;
}
table {
border-collapse: collapse;
width: 100%;
margin-top: 10px;
}
th,
td {
border: 1px solid #ccc;
padding: 6px 10px;
border: 1px solid #999;
padding: 5px 8px;
text-align: right;
}
th {
background: #f5f5f5;
background: #e8e8e8;
font-weight: bold;
font-size: 12px;
}
td.left {
text-align: left;
}
td.right {
text-align: right;
}
tr.total-row td {
background-color: #d8d8d8 !important;
font-weight: bold;
border-top: 2px solid #666;
}
tfoot td {
font-weight: bold;
background: #fafafa;
background: #e0e0e0;
border-top: 2px solid #333;
}
</style>
</head>
<body>
<h2>試算表</h2>
<h2>残高試算表(貸借・損益)</h2>
<div class="search-form">
<label for="dateFrom">開始年月日:</label>
<input type="date" id="dateFrom" />
<label for="dateTo">終了年月日:</label>
<input type="date" id="dateTo" />
<button id="btnSearch">検索</button>
<button id="btnFullYear" class="secondary">全年度表示</button>
</div>
<div class="period-info">
<span id="periodInfo">令和7年 1月 1日 令和7年 12月31日</span>
<span style="margin-left: 20px;">単位: 円</span>
</div>
<table id="trialBalanceTable">
<thead>
@@ -39,9 +115,10 @@
<th class="left">科目コード</th>
<th class="left">科目名称</th>
<th>期首残高</th>
<th>借方合計</th>
<th>贷方合計</th>
<th>借方発生額</th>
<th>貸方発生額</th>
<th>期末残高</th>
<th>構成比(%)</th>
</tr>
</thead>
<tbody>
@@ -54,54 +131,14 @@
<td id="totalDebit">0</td>
<td id="totalCredit">0</td>
<td id="totalClosing">0</td>
<td id="totalRatio">100.00</td>
</tr>
</tfoot>
</table>
<script>
const API_BASE = "http://127.0.0.1:18080"; // 和你后端一致
async function loadTrialBalance() {
const res = await fetch(`${API_BASE}/trial-balance`);
if (!res.ok) {
alert("試算表の取得に失敗しました");
return;
}
const data = await res.json();
const tbody = document.querySelector("#trialBalanceTable tbody");
tbody.innerHTML = "";
data.accounts.forEach((acc) => {
const tr = document.createElement("tr");
tr.innerHTML = `
<td class="left">${acc.account_code}</td>
<td class="left">${acc.account_name}</td>
<td>${Number(acc.opening_balance).toLocaleString()}</td>
<td>${Number(acc.debit).toLocaleString()}</td>
<td>${Number(acc.credit).toLocaleString()}</td>
<td>${Number(acc.closing_balance).toLocaleString()}</td>
`;
tbody.appendChild(tr);
});
// totals
document.getElementById("totalOpening").innerText = Number(
data.totals.opening_balance
).toLocaleString();
document.getElementById("totalDebit").innerText = Number(
data.totals.debit
).toLocaleString();
document.getElementById("totalCredit").innerText = Number(
data.totals.credit
).toLocaleString();
document.getElementById("totalClosing").innerText = Number(
data.totals.closing_balance
).toLocaleString();
}
loadTrialBalance();
<script src="js/trial-balance.js"></script>
</body>
</html>
</script>
</body>
</html>