Files
njts-accounting-core/frontend/journal-edit.html
2026-02-20 15:47:27 +09:00

267 lines
7.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" />
<script src="js/auth.js"></script>
<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" min="1900-01-01" max="2099-12-31" />
<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 = "";
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 || "";
}
// 入力時に合計を自動更新
tr.querySelectorAll("input").forEach((input) => {
input.addEventListener("input", recalc);
input.addEventListener("change", recalc);
});
}
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("借方合計と貸方合計が一致していません");
// 源仕訳IDを親IDとして取得修正の場合
const src = localStorage.getItem("editSource");
const srcData = src ? JSON.parse(src) : null;
// 【修正仕訳】の original_entry_id は、修正元の仕訳ID
// 如果当前是修正版本,传递它的 original_entry_id真正的原始版本
// 如果当前是原始版本,传递它自己的 journal_entry_id
const originalEntryId =
srcData?.original_entry_id || srcData?.journal_entry_id || null;
const payload = {
entry_date: entryDate,
description: desc,
lines,
tax_paid_account_id: null,
tax_received_account_id: null,
rounding_mode: "floor",
original_entry_id: originalEntryId,
};
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 || "登録失敗");
// 登録成功 - 親窓口の検索を再実行してから画面を閉じる
alert(`登録しましたID: ${data.journal_entry_id}`);
localStorage.removeItem("editSource");
// 親窓口(列表页)の searchJournalsを呼び出す
if (window.opener && window.opener.searchJournals) {
window.opener.searchJournals();
}
window.close();
} 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() {
localStorage.removeItem("editSource");
window.close();
}
</script>
</body>
</html>