This commit is contained in:
admin
2026-03-04 23:20:42 +09:00
parent 99d16d8219
commit 31a64d5b59
11 changed files with 674 additions and 201 deletions

View File

@@ -1,4 +1,4 @@
<!doctype html>
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
@@ -129,7 +129,7 @@
}
@media print {
@page {
margin: 20mm 20mm 25mm 20mm;
margin: 5mm 15mm 20mm 15mm;
@bottom-center {
content: "ページ " counter(page);
font-size: 0.75em;
@@ -869,6 +869,40 @@
<script>
const API = "";
// カスタム通知トースト
function showAlert(msg, type) {
const configs = {
success: {
icon: "✔",
iconColor: "#22a84b",
bg: "#f0faf4",
border: "#22a84b",
},
error: {
icon: "✖",
iconColor: "#e53935",
bg: "#fff5f5",
border: "#e53935",
},
warning: {
icon: "⚠",
iconColor: "#e67e00",
bg: "#fffbf0",
border: "#f0a500",
},
};
const c = configs[type] || configs.warning;
const container = document.getElementById("alertToastContainer");
const toast = document.createElement("div");
toast.style.cssText = `pointer-events:auto;display:flex;align-items:flex-start;gap:12px;background:${c.bg};border:1px solid ${c.border};border-left:5px solid ${c.border};border-radius:8px;padding:14px 16px;min-width:280px;max-width:420px;box-shadow:0 4px 16px rgba(0,0,0,0.14);animation:alertSlideIn 0.25s ease;`;
const msgEsc = String(msg).replace(/&/g, "&amp;").replace(/</g, "&lt;");
toast.innerHTML = `<span style="font-size:26px;color:${c.iconColor};line-height:1;flex-shrink:0;">${c.icon}</span><span style="font-size:14px;color:#333;flex:1;white-space:pre-wrap;margin-top:3px;">${msgEsc}</span><button onclick="this.parentElement.remove()" style="background:none;border:none;cursor:pointer;color:#aaa;font-size:20px;line-height:1;padding:0 0 0 8px;flex-shrink:0;">&times;</button>`;
container.appendChild(toast);
setTimeout(() => {
if (toast.parentElement) toast.remove();
}, 4500);
}
// 千分位カンマフォーマット関数
function formatNumberInput(input) {
let value = input.value.replace(/,/g, ""); // 既存のカンマを削除
@@ -1142,7 +1176,7 @@
const res = await fetch(`${API}/accounts`);
if (!res.ok) {
console.error("❌ API 错误:", res.status);
alert("科目一覧の取得に失敗しました");
showAlert("科目一覧の取得に失敗しました", "error");
return;
}
@@ -1796,17 +1830,18 @@
.value.trim();
if (dateInput.validity.badInput) {
alert(
showAlert(
"日付が正しくありません。存在しない日付が入力されています11月31日は無効です",
"warning",
);
return;
}
if (!entryDate) {
alert("仕訳日を入力してください");
showAlert("仕訳日を入力してください", "warning");
return;
}
if (!description) {
alert("摘要を入力してください");
showAlert("摘要を入力してください", "warning");
return;
}
@@ -1824,12 +1859,15 @@
if (debit === 0 && credit === 0) return; // 空行スキップ
if (!accountId) {
alert("科目が選択されていない行があります");
showAlert("科目が選択されていない行があります", "warning");
valid = false;
return;
}
if (debit > 0 && credit > 0) {
alert("1行に借方と貸方の両方を入力することはできません");
showAlert(
"1行に借方と貸方の両方を入力することはできません",
"warning",
);
valid = false;
return;
}
@@ -1848,12 +1886,13 @@
if (!valid) return;
if (lines.length < 2) {
alert("仕訳明細は2行以上必要です");
showAlert("仕訳明細は2行以上必要です", "warning");
return;
}
if (debitSum !== creditSum) {
alert(
showAlert(
`貸借が一致していません(差額:${Math.abs(debitSum - creditSum).toLocaleString()} 円)`,
"warning",
);
return;
}
@@ -1883,7 +1922,7 @@
}
saveDescriptionToHistory(description);
alert("仕訳を登録しました");
showAlert("仕訳を登録しました", "success");
const keep = document.getElementById("detailKeepAfterSubmit").checked;
if (!keep) {
@@ -1893,7 +1932,7 @@
searchJournals();
} catch (e) {
alert("登録エラー: " + e.message);
showAlert("登録エラー: " + e.message, "error");
}
}
@@ -1922,13 +1961,14 @@
).value;
if (entryDateInput.validity.badInput) {
alert(
showAlert(
"日付が正しくありません。存在しない日付が入力されています11月31日は無効です",
"warning",
);
return;
}
if (!entryDate) {
alert("仕訳日を入力してください");
showAlert("仕訳日を入力してください", "warning");
return;
}
@@ -1948,15 +1988,15 @@
);
if (!accountId) {
alert("科目を選択してください");
showAlert("科目を選択してください", "warning");
return;
}
if (!methodId) {
alert("取引手段を選択してください");
showAlert("取引手段を選択してください", "warning");
return;
}
if (amount <= 0) {
alert("金額を入力してください");
showAlert("金額を入力してください", "warning");
return;
}
@@ -1978,6 +2018,7 @@
account_id: accountId,
debit: netAmount,
credit: 0,
...(taxRateNum !== null ? { tax_rate: taxRateNum } : {}),
});
// 借方: 仮払消費税等(税額)※税額がある場合のみ
@@ -1985,7 +2026,7 @@
const taxPaidAcc =
taxPaidAccounts.length > 0 ? taxPaidAccounts[0] : null;
if (!taxPaidAcc) {
alert("仮払消費税等の勘定科目が見つかりません");
showAlert("仮払消費税等の勘定科目が見つかりません", "error");
return;
}
lines.push({
@@ -2015,6 +2056,7 @@
account_id: accountId,
debit: 0,
credit: netAmount,
...(taxRateNum !== null ? { tax_rate: taxRateNum } : {}),
});
// 貸方: 仮受消費税等(税額)※税額がある場合のみ
@@ -2022,7 +2064,7 @@
const taxReceivedAcc =
taxReceivedAccounts.length > 0 ? taxReceivedAccounts[0] : null;
if (!taxReceivedAcc) {
alert("仮受消費税等の勘定科目が見つかりません");
showAlert("仮受消費税等の勘定科目が見つかりません", "error");
return;
}
lines.push({
@@ -2060,7 +2102,7 @@
const errorMsg =
errorData.detail || `登録に失敗しました (HTTP ${res.status})`;
console.error("Error:", errorData);
alert(errorMsg);
showAlert(errorMsg, "error");
return;
}
@@ -2069,7 +2111,7 @@
// 摘要を履歴に保存
saveDescriptionToHistory(description);
alert(`登録完了ID=${result.journal_entry_id}`);
showAlert(`登録完了ID=${result.journal_entry_id}`, "success");
// 「登録後に明細を保持する」チェックボックスを確認
const keepDetails = document.getElementById(
@@ -2092,8 +2134,9 @@
searchJournals();
} catch (error) {
console.error("送信エラー:", error);
alert(
showAlert(
`エラーが発生しました: ${error.message}\n\nサーバーが起動しているか確認してください。`,
"error",
);
}
}
@@ -2387,6 +2430,16 @@
const conditionsText =
conditions.length > 0 ? conditions.join(" | ") : "検索条件: すべて";
// 印刷日時
const printedAt = new Date().toLocaleString("ja-JP", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
});
// 月ごとにテーブルを生成(印刷用)
Object.keys(monthGroups)
.sort()
@@ -2409,11 +2462,14 @@
<table style="margin-bottom: 20px;">
<thead>
<tr>
<th colspan="6" style="text-align: center; font-size: 1.2em; padding: 8px; border: none; background: transparent;">仕訳検索結果</th>
<th colspan="5" style="text-align: center; font-size: 1.2em; padding: 8px; border: none; background: transparent;">仕訳検索結果</th>
</tr>
<tr>
<th colspan="5" style="text-align: right; font-weight: normal; font-size: 0.85em; padding: 2px 4px; border: none; background: transparent;">印刷日時: ${printedAt}</th>
</tr>
<tr>
<th colspan="3" style="text-align: left; font-weight: normal; font-size: 0.9em; padding: 4px; border: none; background: transparent;">${conditionsText}</th>
<th colspan="3" style="text-align: right; font-weight: normal; font-size: 0.9em; padding: 4px; border: none; background: transparent;">${monthDisplay}</th>
<th colspan="2" style="text-align: right; font-weight: normal; font-size: 0.9em; padding: 4px; border: none; background: transparent;">${monthDisplay}</th>
</tr>
<tr>
<th style="white-space: nowrap;">日付</th>
@@ -2421,7 +2477,6 @@
<th style="white-space: nowrap;">借方合計</th>
<th style="white-space: nowrap;">貸方合計</th>
<th>税率</th>
<th style="white-space: nowrap;">操作</th>
</tr>
</thead>
<tbody></tbody>
@@ -2452,20 +2507,6 @@
<td style="text-align:center">${
e.tax_rates ? e.tax_rates + "%" : ""
}</td>
<td style="white-space: nowrap;">
<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);
});
@@ -2478,7 +2519,7 @@
<td colspan="2" style="text-align:right; white-space: nowrap;">合計:</td>
<td style="text-align:right; white-space: nowrap;">${monthDebitTotal.toLocaleString()}</td>
<td style="text-align:right; white-space: nowrap;">${monthCreditTotal.toLocaleString()}</td>
<td colspan="2"></td>
<td></td>
`;
tbody.appendChild(totalRow);
@@ -2502,7 +2543,7 @@
</div>
`;
}
alert(`検索に失敗しました: ${err.message}`);
showAlert(`検索に失敗しました: ${err.message}`, "error");
}
}
@@ -2722,7 +2763,7 @@
</div>
`;
}
alert(`検索に失敗しました: ${err.message}`);
showAlert(`検索に失敗しました: ${err.message}`, "error");
}
}
@@ -2737,7 +2778,7 @@
try {
const res = await fetch(`${API}/journal-entries/${id}`);
if (!res.ok) {
alert("仕訳の取得に失敗しました");
showAlert("仕訳の取得に失敗しました", "error");
return;
}
@@ -2916,14 +2957,13 @@
const dateInput = document.getElementById("entryDate");
dateInput.focus();
alert(
`仕訳を複製しました。\n\n` +
`※ このコピーは新規仕訳として独立した記録になります。\n` +
`※ 日付と摘要を必要に応じて修正してから登録してください。`,
showAlert(
`仕訳を複製しました。\n\n※ このコピーは新規仕訳として独立した記録になります。\n※ 日付と摘要を必要に応じて修正してから登録してください。`,
"success",
);
} catch (error) {
console.error("Error:", error);
alert(`エラーが発生しました: ${error.message}`);
showAlert(`エラーが発生しました: ${error.message}`, "error");
}
}
@@ -2954,7 +2994,7 @@
async function deleteJournalEntry(id) {
const reason = prompt("削除理由を入力してください:");
if (!reason || reason.trim() === "") {
alert("削除理由の入力が必要です");
showAlert("削除理由の入力が必要です", "warning");
return;
}
@@ -2970,11 +3010,11 @@
const data = await res.json();
if (!res.ok) {
alert(data.detail || "削除に失敗しました");
showAlert(data.detail || "削除に失敗しました", "error");
return;
}
alert("削除しました");
showAlert("削除しました", "success");
searchJournals(); // リストを再読み込み
}
@@ -3056,5 +3096,30 @@
await searchJournals();
});
</script>
<div
id="alertToastContainer"
style="
position: fixed;
top: 24px;
right: 24px;
z-index: 9999;
display: flex;
flex-direction: column;
gap: 10px;
pointer-events: none;
"
></div>
<style>
@keyframes alertSlideIn {
from {
opacity: 0;
transform: translateX(40px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
</style>
</body>
</html>