Files
njts-accounting-core/frontend/journal-edit.html
2026-01-12 20:59:52 +09:00

244 lines
6.7 KiB
HTML
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.
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>修正仕訳入力</title>
<style>
body {
font-family: sans-serif;
margin: 24px;
}
table {
border-collapse: collapse;
width: 100%;
margin-top: 12px;
}
th,
td {
border: 1px solid #ccc;
padding: 6px;
text-align: center;
}
th {
background: #f5f5f5;
}
input,
select,
button {
padding: 6px;
}
.right {
text-align: right;
}
.error {
color: #c00;
margin-top: 8px;
}
.ok {
color: #090;
margin-top: 8px;
}
</style>
</head>
<body>
<h2>修正仕訳入力</h2>
<label>仕訳日</label>
<input type="date" id="entryDate" />
<label>摘要</label>
<input type="text" id="description" style="width: 60%" />
<table id="linesTable">
<thead>
<tr>
<th>科目</th>
<th>借方</th>
<th>貸方</th>
<th>操作</th>
</tr>
</thead>
<tbody></tbody>
<tfoot>
<tr>
<th>合計</th>
<th id="debitTotal" class="right">0</th>
<th id="creditTotal" class="right">0</th>
<th></th>
</tr>
</tfoot>
</table>
<div style="margin-top: 12px">
<button onclick="addRow()"> 行追加</button>
<button onclick="submitEntry()">修正版仕訳を登録</button>
<button onclick="cancelEdit()" style="background-color: #f0f0f0">
キャンセル
</button>
</div>
<div id="result"></div>
<script>
const API = "http://127.0.0.1:18080";
let accounts = [];
// ------------------------------------
// 初期化
// ------------------------------------
async function init() {
// 科目取得
const res = await fetch(`${API}/accounts`);
const data = await res.json();
accounts = data.items ?? data;
// 元仕訳取得
const src = localStorage.getItem("editSource");
if (!src) {
showError("修正元の仕訳データが見つかりません");
return;
}
const srcData = JSON.parse(src);
// ヘッダ反映
document.getElementById("entryDate").value = srcData.entry_date;
document.getElementById(
"description"
).value = `【修正後】${srcData.description}`;
// 明細反映
srcData.lines.forEach((l) => addRow(l));
recalc();
}
init();
// ------------------------------------
// 行操作
// ------------------------------------
function addRow(line = null) {
const tbody = document.querySelector("#linesTable tbody");
const tr = document.createElement("tr");
tr.innerHTML = `
<td>
<select>
${accounts
.map(
(a) =>
`<option value="${a.account_id}">${a.account_code} ${a.account_name}</option>`
)
.join("")}
</select>
</td>
<td><input type="number" min="0"></td>
<td><input type="number" min="0"></td>
<td><button onclick="removeRow(this)">削除</button></td>
`;
tbody.appendChild(tr);
if (line) {
tr.querySelector("select").value = line.account_id;
tr.querySelectorAll("input")[0].value = line.debit || "";
tr.querySelectorAll("input")[1].value = line.credit || "";
}
}
function removeRow(btn) {
btn.closest("tr").remove();
recalc();
}
// ------------------------------------
// 合計再計算
// ------------------------------------
function recalc() {
let d = 0,
c = 0;
document.querySelectorAll("#linesTable tbody tr").forEach((tr) => {
d += Number(tr.querySelectorAll("input")[0].value || 0);
c += Number(tr.querySelectorAll("input")[1].value || 0);
});
document.getElementById("debitTotal").textContent = d.toLocaleString();
document.getElementById("creditTotal").textContent = c.toLocaleString();
}
// ------------------------------------
// 登録
// ------------------------------------
async function submitEntry() {
const result = document.getElementById("result");
result.textContent = "";
result.className = "";
const entryDate = document.getElementById("entryDate").value;
const desc = document.getElementById("description").value;
if (!entryDate || !desc) {
showError("日付と摘要は必須です");
return;
}
const lines = [];
let d = 0,
c = 0;
document.querySelectorAll("#linesTable tbody tr").forEach((tr) => {
const accountId = Number(tr.querySelector("select").value);
const debit = Number(tr.querySelectorAll("input")[0].value || 0);
const credit = Number(tr.querySelectorAll("input")[1].value || 0);
if (debit === 0 && credit === 0) return;
lines.push({ account_id: accountId, debit, credit });
d += debit;
c += credit;
});
if (lines.length < 2) return showError("仕訳行は2行以上必要です");
if (d !== c) return showError("借方合計と貸方合計が一致していません");
const payload = {
entry_date: entryDate,
description: desc,
lines,
tax_paid_account_id: null,
tax_received_account_id: null,
rounding_mode: "floor",
};
try {
const res = await fetch(`${API}/journal-entries`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
const data = await res.json();
if (!res.ok) return showError(data.detail || "登録失敗");
showOk(`修正版仕訳を登録しましたID: ${data.journal_entry_id}`);
localStorage.removeItem("editSource");
} catch {
showError("通信エラー");
}
}
function showError(msg) {
const r = document.getElementById("result");
r.textContent = msg;
r.className = "error";
}
function showOk(msg) {
const r = document.getElementById("result");
r.textContent = msg;
r.className = "ok";
}
function cancelEdit() {
if (confirm("編集をキャンセルして一覧画面に戻りますか?")) {
localStorage.removeItem("editSource");
window.location.href = "journal-list.html";
}
}
</script>
</body>
</html>