login画面追加
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="js/auth.js"></script>
|
||||
<title>科目マスタ</title>
|
||||
<link rel="stylesheet" href="css/style.css" />
|
||||
</head>
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>NJTS 会計システム - メイン</title>
|
||||
<link rel="stylesheet" href="css/style.css" />
|
||||
<script src="js/auth.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, -apple-system, "Yu Gothic UI",
|
||||
"Hiragino Kaku Gothic ProN", "メイリオ", sans-serif;
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
"Yu Gothic UI",
|
||||
"Hiragino Kaku Gothic ProN",
|
||||
"メイリオ",
|
||||
sans-serif;
|
||||
padding: 24px;
|
||||
}
|
||||
.grid {
|
||||
@@ -46,11 +52,34 @@
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
.logout-btn {
|
||||
padding: 8px 12px;
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
}
|
||||
.logout-btn:hover {
|
||||
background: #c82333;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>NJTS</h1>
|
||||
<div class="user-info">
|
||||
<span>ユーザー: <strong id="username">読み込み中...</strong></span>
|
||||
<button class="logout-btn" onclick="logout()">ログアウト</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="grid">
|
||||
@@ -78,6 +107,14 @@
|
||||
</main>
|
||||
|
||||
<script>
|
||||
// ユーザー情報を表示
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const user = getCurrentUser();
|
||||
if (user) {
|
||||
document.getElementById("username").textContent = user.username;
|
||||
}
|
||||
});
|
||||
|
||||
// dropdown toggle
|
||||
const toggle = document.getElementById("accountingToggle");
|
||||
const menu = document.getElementById("accountingMenu");
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="js/auth.js"></script>
|
||||
<title>修正仕訳入力</title>
|
||||
<style>
|
||||
body {
|
||||
@@ -102,9 +103,8 @@
|
||||
|
||||
// ヘッダ反映
|
||||
document.getElementById("entryDate").value = srcData.entry_date;
|
||||
document.getElementById(
|
||||
"description"
|
||||
).value = `【修正後】${srcData.description}`;
|
||||
document.getElementById("description").value =
|
||||
`【修正後】${srcData.description}`;
|
||||
|
||||
// 明細反映
|
||||
srcData.lines.forEach((l) => addRow(l));
|
||||
@@ -126,7 +126,7 @@
|
||||
${accounts
|
||||
.map(
|
||||
(a) =>
|
||||
`<option value="${a.account_id}">${a.account_code} ${a.account_name}</option>`
|
||||
`<option value="${a.account_id}">${a.account_code} ${a.account_name}</option>`,
|
||||
)
|
||||
.join("")}
|
||||
</select>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="js/auth.js"></script>
|
||||
<title>仕訳入力</title>
|
||||
<style>
|
||||
body {
|
||||
@@ -393,12 +394,12 @@
|
||||
|
||||
// 仮払消費税等(あなたの DB では 505 は「未払消費税等」)
|
||||
taxPaidAccounts = accounts.filter((a) =>
|
||||
a.account_name.includes("仮払")
|
||||
a.account_name.includes("仮払"),
|
||||
);
|
||||
|
||||
// 仮受消費税等
|
||||
taxReceivedAccounts = accounts.filter((a) =>
|
||||
a.account_name.includes("仮受")
|
||||
a.account_name.includes("仮受"),
|
||||
);
|
||||
|
||||
setupTaxSelectors();
|
||||
@@ -432,7 +433,7 @@
|
||||
.addEventListener("change", () => {
|
||||
// 端数処理変更時は全ての税行を再計算
|
||||
const rows = document.querySelectorAll(
|
||||
"#linesTable tbody tr:not(.tax-row)"
|
||||
"#linesTable tbody tr:not(.tax-row)",
|
||||
);
|
||||
rows.forEach((row) => handleTax(row));
|
||||
});
|
||||
@@ -452,7 +453,7 @@
|
||||
optgroup.label = group.label;
|
||||
|
||||
const groupAccounts = accounts.filter((a) =>
|
||||
group.codes.includes(a.account_code)
|
||||
group.codes.includes(a.account_code),
|
||||
);
|
||||
|
||||
groupAccounts.forEach((acc) => {
|
||||
@@ -471,7 +472,7 @@
|
||||
// -----------------------------
|
||||
function loadDescriptionHistory() {
|
||||
const history = JSON.parse(
|
||||
localStorage.getItem("descriptionHistory") || "[]"
|
||||
localStorage.getItem("descriptionHistory") || "[]",
|
||||
);
|
||||
const datalist = document.getElementById("descriptionHistory");
|
||||
datalist.innerHTML = "";
|
||||
@@ -487,7 +488,7 @@
|
||||
if (!description || description.trim() === "") return;
|
||||
|
||||
let history = JSON.parse(
|
||||
localStorage.getItem("descriptionHistory") || "[]"
|
||||
localStorage.getItem("descriptionHistory") || "[]",
|
||||
);
|
||||
|
||||
// 重複を避け、先頭に追加
|
||||
@@ -512,7 +513,7 @@
|
||||
taxPaidAccounts
|
||||
.map(
|
||||
(a) =>
|
||||
`<option value="${a.account_id}">${a.account_code} ${a.account_name}</option>`
|
||||
`<option value="${a.account_id}">${a.account_code} ${a.account_name}</option>`,
|
||||
)
|
||||
.join("");
|
||||
|
||||
@@ -521,7 +522,7 @@
|
||||
taxReceivedAccounts
|
||||
.map(
|
||||
(a) =>
|
||||
`<option value="${a.account_id}">${a.account_code} ${a.account_name}</option>`
|
||||
`<option value="${a.account_id}">${a.account_code} ${a.account_name}</option>`,
|
||||
)
|
||||
.join("");
|
||||
}
|
||||
@@ -660,7 +661,7 @@
|
||||
balanceStatus.style.background = "";
|
||||
} else {
|
||||
balanceStatus.textContent = `✗ 差額: ${Math.abs(
|
||||
totalDebit - totalCredit
|
||||
totalDebit - totalCredit,
|
||||
).toLocaleString()}`;
|
||||
balanceStatus.style.background = "#f8d7da";
|
||||
balanceStatus.style.color = "#721c24";
|
||||
@@ -677,10 +678,10 @@
|
||||
const taxType = row.querySelector(".taxType").value;
|
||||
const taxDir = row.querySelector(".taxDirection").value;
|
||||
const debit = Number(
|
||||
row.querySelector(".debit").value.replace(/,/g, "") || 0
|
||||
row.querySelector(".debit").value.replace(/,/g, "") || 0,
|
||||
);
|
||||
const credit = Number(
|
||||
row.querySelector(".credit").value.replace(/,/g, "") || 0
|
||||
row.querySelector(".credit").value.replace(/,/g, "") || 0,
|
||||
);
|
||||
|
||||
if (taxType === "none" || taxDir === "none") {
|
||||
@@ -722,7 +723,7 @@
|
||||
addRow(
|
||||
true,
|
||||
{ accountId: taxAcc, amount: taxAmount, side: "debit" },
|
||||
parentId
|
||||
parentId,
|
||||
);
|
||||
}
|
||||
if (taxDir === "received") {
|
||||
@@ -734,7 +735,7 @@
|
||||
addRow(
|
||||
true,
|
||||
{ accountId: taxAcc, amount: taxAmount, side: "credit" },
|
||||
parentId
|
||||
parentId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -781,10 +782,10 @@
|
||||
|
||||
const accountId = Number(row.querySelector(".account").value);
|
||||
const debit = Number(
|
||||
row.querySelector(".debit").value.replace(/,/g, "") || 0
|
||||
row.querySelector(".debit").value.replace(/,/g, "") || 0,
|
||||
);
|
||||
const credit = Number(
|
||||
row.querySelector(".credit").value.replace(/,/g, "") || 0
|
||||
row.querySelector(".credit").value.replace(/,/g, "") || 0,
|
||||
);
|
||||
const taxType = row.querySelector(".taxType").value;
|
||||
const taxDirection = row.querySelector(".taxDirection").value;
|
||||
@@ -818,7 +819,7 @@
|
||||
// 税科目(仮払消費税等・仮受消費税等)が存在するかチェック
|
||||
const hasTaxAccount = lines.some((line) => {
|
||||
const account = accounts.find(
|
||||
(a) => a.account_id === line.account_id
|
||||
(a) => a.account_id === line.account_id,
|
||||
);
|
||||
return (
|
||||
account &&
|
||||
@@ -829,7 +830,7 @@
|
||||
|
||||
// 税科目が存在し、かつ税情報を持つ行がある場合は警告
|
||||
const hasTaxInfo = lines.some(
|
||||
(line) => line.tax_rate != null && line.tax_direction != null
|
||||
(line) => line.tax_rate != null && line.tax_direction != null,
|
||||
);
|
||||
|
||||
if (hasTaxAccount && hasTaxInfo) {
|
||||
@@ -838,7 +839,7 @@
|
||||
"警告:仮払消費税等・仮受消費税等の行がすでに存在していますが、他の行に税区分・税方向が設定されています。\n" +
|
||||
"このまま登録すると税が二重計算され、借貸不平衡になります。\n\n" +
|
||||
"税区分・税方向をすべて「対象外」に設定してから登録することをお勧めします。\n\n" +
|
||||
"このまま登録しますか?"
|
||||
"このまま登録しますか?",
|
||||
)
|
||||
) {
|
||||
return;
|
||||
@@ -903,7 +904,7 @@
|
||||
} catch (error) {
|
||||
console.error("送信エラー:", error);
|
||||
alert(
|
||||
`エラーが発生しました: ${error.message}\n\nサーバーが起動しているか確認してください。`
|
||||
`エラーが発生しました: ${error.message}\n\nサーバーが起動しているか確認してください。`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -955,7 +956,7 @@
|
||||
|
||||
try {
|
||||
await callApi(
|
||||
`/month-locks/unlock?fiscal_year=${year}&month=${month}`
|
||||
`/month-locks/unlock?fiscal_year=${year}&month=${month}`,
|
||||
);
|
||||
el.innerText = `✅ ${year}年${month}月 已解锁`;
|
||||
await refreshMonthLocks(); // 可选:自动刷新列表
|
||||
@@ -985,8 +986,8 @@
|
||||
(x) =>
|
||||
`${x.fiscal_year}-${String(x.month).padStart(
|
||||
2,
|
||||
"0"
|
||||
)} locked_by=${x.locked_by ?? ""}`
|
||||
"0",
|
||||
)} locked_by=${x.locked_by ?? ""}`,
|
||||
)
|
||||
.join("\n");
|
||||
} catch (e) {
|
||||
@@ -1005,7 +1006,7 @@
|
||||
const data = await res.json();
|
||||
|
||||
return (data || []).some(
|
||||
(x) => x.fiscal_year === year && x.month === month && x.is_locked
|
||||
(x) => x.fiscal_year === year && x.month === month && x.is_locked,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1049,7 +1050,7 @@
|
||||
|
||||
// 最近使った科目を表示
|
||||
const recentAccountIds = JSON.parse(
|
||||
localStorage.getItem("recentAccounts") || "[]"
|
||||
localStorage.getItem("recentAccounts") || "[]",
|
||||
);
|
||||
if (recentAccountIds.length > 0) {
|
||||
html += `<optgroup label="⭐ 最近使った科目">`;
|
||||
@@ -1080,7 +1081,7 @@
|
||||
// 未分组的其他科目
|
||||
const groupedCodes = ACCOUNT_GROUPS.flatMap((g) => g.codes);
|
||||
const others = accounts.filter(
|
||||
(a) => !groupedCodes.includes(a.account_code)
|
||||
(a) => !groupedCodes.includes(a.account_code),
|
||||
);
|
||||
|
||||
if (others.length > 0) {
|
||||
@@ -1101,7 +1102,7 @@
|
||||
if (!accountId) return;
|
||||
|
||||
let recentAccounts = JSON.parse(
|
||||
localStorage.getItem("recentAccounts") || "[]"
|
||||
localStorage.getItem("recentAccounts") || "[]",
|
||||
);
|
||||
|
||||
// 重複を除去して先頭に追加
|
||||
@@ -1127,7 +1128,7 @@
|
||||
const key = document.getElementById("searchKeyword").value;
|
||||
const accountId = document.getElementById("searchAccountId").value;
|
||||
const includeHistory = document.getElementById(
|
||||
"searchIncludeHistory"
|
||||
"searchIncludeHistory",
|
||||
).checked;
|
||||
|
||||
const qs = new URLSearchParams();
|
||||
@@ -1191,7 +1192,7 @@
|
||||
{
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const section = document.createElement("div");
|
||||
@@ -1287,7 +1288,7 @@
|
||||
const key = document.getElementById("searchKeyword").value;
|
||||
const accountId = document.getElementById("searchAccountId").value;
|
||||
const includeHistory = document.getElementById(
|
||||
"searchIncludeHistory"
|
||||
"searchIncludeHistory",
|
||||
).checked;
|
||||
|
||||
// 検索条件を保存
|
||||
@@ -1299,7 +1300,7 @@
|
||||
keyword: key,
|
||||
accountId: accountId,
|
||||
includeHistory: includeHistory,
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
const qs = new URLSearchParams();
|
||||
@@ -1358,7 +1359,7 @@
|
||||
{
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const section = document.createElement("div");
|
||||
@@ -1512,10 +1513,10 @@
|
||||
|
||||
console.log("コピーされたデータ:", data); // デバッグ用
|
||||
console.log(
|
||||
`${data.lines.length}行をコピーしました。税区分・税方向はすべて「対象外」に設定されています。`
|
||||
`${data.lines.length}行をコピーしました。税区分・税方向はすべて「対象外」に設定されています。`,
|
||||
);
|
||||
alert(
|
||||
`仕訳を複製しました(${data.lines.length}行)。\n税の自動計算を防ぐため、すべての行の税区分・税方向を「対象外」に設定しています。\n必要に応じて修正してから登録してください。`
|
||||
`仕訳を複製しました(${data.lines.length}行)。\n税の自動計算を防ぐため、すべての行の税区分・税方向を「対象外」に設定しています。\n必要に応じて修正してから登録してください。`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
@@ -1529,7 +1530,7 @@
|
||||
async function reverseAndEdit(id) {
|
||||
if (
|
||||
!confirm(
|
||||
"この仕訳の逆仕訳を作成し、修正仕訳入力画面を開きます。よろしいですか?"
|
||||
"この仕訳の逆仕訳を作成し、修正仕訳入力画面を開きます。よろしいですか?",
|
||||
)
|
||||
)
|
||||
return;
|
||||
@@ -1543,14 +1544,14 @@
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}));
|
||||
alert(
|
||||
data.detail || `逆仕訳の作成に失敗しました (HTTP ${res.status})`
|
||||
data.detail || `逆仕訳の作成に失敗しました (HTTP ${res.status})`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
alert(
|
||||
`逆仕訳を作成しました(ID: ${data.reversed_journal_entry_id})`
|
||||
`逆仕訳を作成しました(ID: ${data.reversed_journal_entry_id})`,
|
||||
);
|
||||
|
||||
// ② 元仕訳内容を取得して修正画面へ遷移
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="js/auth.js"></script>
|
||||
<title>仕訳一覧</title>
|
||||
<style>
|
||||
body {
|
||||
@@ -63,7 +64,7 @@
|
||||
fromDate: from,
|
||||
toDate: to,
|
||||
keyword: key,
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
const qs = new URLSearchParams();
|
||||
@@ -106,7 +107,7 @@
|
||||
async function reverseAndEdit(id) {
|
||||
if (
|
||||
!confirm(
|
||||
"この仕訳の逆仕訳を作成し、修正仕訳入力画面を開きます。よろしいですか?"
|
||||
"この仕訳の逆仕訳を作成し、修正仕訳入力画面を開きます。よろしいですか?",
|
||||
)
|
||||
)
|
||||
return;
|
||||
@@ -120,14 +121,14 @@
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}));
|
||||
alert(
|
||||
data.detail || `逆仕訳の作成に失敗しました (HTTP ${res.status})`
|
||||
data.detail || `逆仕訳の作成に失敗しました (HTTP ${res.status})`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
alert(
|
||||
`逆仕訳を作成しました(ID: ${data.reversed_journal_entry_id})`
|
||||
`逆仕訳を作成しました(ID: ${data.reversed_journal_entry_id})`,
|
||||
);
|
||||
|
||||
// ② 元仕訳内容を取得して修正画面へ遷移
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="js/auth.js"></script>
|
||||
<title>仕訳詳細</title>
|
||||
<style>
|
||||
body {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="js/auth.js"></script>
|
||||
<title>仕訳入力</title>
|
||||
<link rel="stylesheet" href="css/style.css" />
|
||||
</head>
|
||||
|
||||
41
frontend/js/auth.js
Normal file
41
frontend/js/auth.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 认证和会话管理
|
||||
*/
|
||||
|
||||
// 检查用户是否已登录
|
||||
function checkAuth() {
|
||||
const user = localStorage.getItem("currentUser");
|
||||
if (!user) {
|
||||
// 用户未登录,重定向到登录页面
|
||||
window.location.href = "/login.html";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 获取当前登录的用户信息
|
||||
function getCurrentUser() {
|
||||
const user = localStorage.getItem("currentUser");
|
||||
return user ? JSON.parse(user) : null;
|
||||
}
|
||||
|
||||
// 保存用户信息(登录时使用)
|
||||
function setCurrentUser(userData) {
|
||||
localStorage.setItem("currentUser", JSON.stringify(userData));
|
||||
}
|
||||
|
||||
// 注销用户
|
||||
function logout() {
|
||||
localStorage.removeItem("currentUser");
|
||||
window.location.href = "/login.html";
|
||||
}
|
||||
|
||||
// 在页面加载时检查认证(仅在非登录页面使用)
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const currentPage = window.location.pathname;
|
||||
|
||||
// 排除登录页面的检查
|
||||
if (!currentPage.includes("login.html")) {
|
||||
checkAuth();
|
||||
}
|
||||
});
|
||||
@@ -1,7 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="js/auth.js"></script>
|
||||
<title>元帳</title>
|
||||
<link rel="stylesheet" href="css/style.css" />
|
||||
</head>
|
||||
|
||||
411
frontend/login.html
Normal file
411
frontend/login.html
Normal file
@@ -0,0 +1,411 @@
|
||||
<!doctype html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>NJTS 会計システム - ログイン</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
"Yu Gothic UI",
|
||||
"Hiragino Kaku Gothic ProN",
|
||||
"メイリオ",
|
||||
sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.logo h1 {
|
||||
font-size: 28px;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.logo p {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"] {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
input[type="text"]:focus,
|
||||
input[type="password"]:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
padding: 12px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.btn-login {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-login:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.btn-register {
|
||||
background: transparent;
|
||||
color: #667eea;
|
||||
border: 2px solid #667eea;
|
||||
}
|
||||
|
||||
.btn-register:hover {
|
||||
background: #f0f0ff;
|
||||
}
|
||||
|
||||
.message {
|
||||
padding: 12px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
display: none;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.message.error {
|
||||
background: #fee;
|
||||
color: #c33;
|
||||
border: 1px solid #fcc;
|
||||
}
|
||||
|
||||
.message.success {
|
||||
background: #efe;
|
||||
color: #3c3;
|
||||
border: 1px solid #cfc;
|
||||
}
|
||||
|
||||
.toggle-form {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.toggle-form a {
|
||||
color: #667eea;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.toggle-form a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.form {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.form.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.loading {
|
||||
display: none;
|
||||
text-align: center;
|
||||
color: #667eea;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<div class="logo">
|
||||
<h1>NJTS</h1>
|
||||
<p>会計システム</p>
|
||||
</div>
|
||||
|
||||
<!-- ログインフォーム -->
|
||||
<div id="loginForm" class="form active">
|
||||
<div id="messageLogin" class="message"></div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="loginUsername">ユーザー名</label>
|
||||
<input
|
||||
type="text"
|
||||
id="loginUsername"
|
||||
placeholder="ユーザー名を入力"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="loginPassword">パスワード</label>
|
||||
<input
|
||||
type="password"
|
||||
id="loginPassword"
|
||||
placeholder="パスワードを入力"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<button class="btn-login" onclick="handleLogin()">ログイン</button>
|
||||
</div>
|
||||
|
||||
<div class="toggle-form">
|
||||
新規登録?
|
||||
<a onclick="toggleForm()">アカウント作成</a>
|
||||
</div>
|
||||
|
||||
<div class="loading" id="loginLoading">処理中...</div>
|
||||
</div>
|
||||
|
||||
<!-- 登録フォーム -->
|
||||
<div id="registerForm" class="form">
|
||||
<div id="messageRegister" class="message"></div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="regUsername">ユーザー名</label>
|
||||
<input
|
||||
type="text"
|
||||
id="regUsername"
|
||||
placeholder="ユーザー名を入力"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="regPassword">パスワード</label>
|
||||
<input
|
||||
type="password"
|
||||
id="regPassword"
|
||||
placeholder="6文字以上で設定"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<button class="btn-login" onclick="handleRegister()">登録</button>
|
||||
<button class="btn-register" onclick="toggleForm()">
|
||||
キャンセル
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="loading" id="registerLoading">処理中...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleForm() {
|
||||
const loginForm = document.getElementById("loginForm");
|
||||
const registerForm = document.getElementById("registerForm");
|
||||
|
||||
loginForm.classList.toggle("active");
|
||||
registerForm.classList.toggle("active");
|
||||
|
||||
// メッセージをリセット
|
||||
clearMessage("messageLogin");
|
||||
clearMessage("messageRegister");
|
||||
}
|
||||
|
||||
function showMessage(elementId, message, isError = false) {
|
||||
const messageEl = document.getElementById(elementId);
|
||||
messageEl.textContent = message;
|
||||
messageEl.className = `message ${isError ? "error" : "success"}`;
|
||||
messageEl.style.display = "block";
|
||||
}
|
||||
|
||||
function clearMessage(elementId) {
|
||||
const messageEl = document.getElementById(elementId);
|
||||
messageEl.style.display = "none";
|
||||
messageEl.textContent = "";
|
||||
}
|
||||
|
||||
async function handleLogin() {
|
||||
const username = document.getElementById("loginUsername").value;
|
||||
const password = document.getElementById("loginPassword").value;
|
||||
|
||||
if (!username || !password) {
|
||||
showMessage(
|
||||
"messageLogin",
|
||||
"ユーザー名とパスワードを入力してください",
|
||||
true,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const loadingEl = document.getElementById("loginLoading");
|
||||
loadingEl.style.display = "block";
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/users/login", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ username, password }),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
// ユーザー情報をlocalStorageに保存
|
||||
localStorage.setItem("currentUser", JSON.stringify(data));
|
||||
showMessage("messageLogin", "ログインに成功しました!", false);
|
||||
// 2秒後にメインページにリダイレクト
|
||||
setTimeout(() => {
|
||||
window.location.href = "/index.html";
|
||||
}, 2000);
|
||||
} else {
|
||||
showMessage(
|
||||
"messageLogin",
|
||||
data.detail || "ログインに失敗しました",
|
||||
true,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
showMessage(
|
||||
"messageLogin",
|
||||
"エラーが発生しました: " + error.message,
|
||||
true,
|
||||
);
|
||||
} finally {
|
||||
loadingEl.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRegister() {
|
||||
const username = document.getElementById("regUsername").value;
|
||||
const password = document.getElementById("regPassword").value;
|
||||
|
||||
if (!username || !password) {
|
||||
showMessage(
|
||||
"messageRegister",
|
||||
"ユーザー名とパスワードを入力してください",
|
||||
true,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.length < 6) {
|
||||
showMessage(
|
||||
"messageRegister",
|
||||
"パスワードは6文字以上である必要があります",
|
||||
true,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const loadingEl = document.getElementById("registerLoading");
|
||||
loadingEl.style.display = "block";
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/users/register", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ username, password }),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
showMessage(
|
||||
"messageRegister",
|
||||
"登録が完了しました。ログインしてください",
|
||||
false,
|
||||
);
|
||||
setTimeout(() => {
|
||||
toggleForm();
|
||||
document.getElementById("loginUsername").value = username;
|
||||
document.getElementById("loginPassword").value = "";
|
||||
}, 2000);
|
||||
} else {
|
||||
showMessage(
|
||||
"messageRegister",
|
||||
data.detail || "登録に失敗しました",
|
||||
true,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
showMessage(
|
||||
"messageRegister",
|
||||
"エラーが発生しました: " + error.message,
|
||||
true,
|
||||
);
|
||||
} finally {
|
||||
loadingEl.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
// Enterキーでログイン/登録
|
||||
document.addEventListener("keypress", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
const loginForm = document.getElementById("loginForm");
|
||||
if (loginForm.classList.contains("active")) {
|
||||
handleLogin();
|
||||
} else {
|
||||
handleRegister();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,7 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="js/auth.js"></script>
|
||||
<title>期首残高入力</title>
|
||||
<link rel="stylesheet" href="css/style.css" />
|
||||
</head>
|
||||
@@ -39,7 +40,7 @@
|
||||
<br />
|
||||
|
||||
<button onclick="saveOpeningBalances()">保存</button>
|
||||
<button onclick="location.href='trial-balance.html'">← 戻る</button>
|
||||
<button onclick="location.href = 'trial-balance.html'">← 戻る</button>
|
||||
|
||||
<script src="js/api.js"></script>
|
||||
<script src="js/opening_balance.js"></script>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="js/auth.js"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>給与管理 - 月次給与計算</title>
|
||||
<link rel="stylesheet" href="/css/style.css" />
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="js/auth.js"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>給与管理 - 従業員管理</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="js/auth.js"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>給与管理 - 給与設定</title>
|
||||
<link rel="stylesheet" href="/css/style.css" />
|
||||
@@ -213,7 +214,7 @@
|
||||
async function loadEmployees() {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/employees/?is_active=true`
|
||||
`${API_BASE}/payroll/employees/?is_active=true`,
|
||||
);
|
||||
employees = await response.json();
|
||||
|
||||
@@ -223,7 +224,7 @@
|
||||
employees
|
||||
.map(
|
||||
(emp) =>
|
||||
`<option value="${emp.employee_id}">${emp.employee_code} - ${emp.name}</option>`
|
||||
`<option value="${emp.employee_id}">${emp.employee_code} - ${emp.name}</option>`,
|
||||
)
|
||||
.join("");
|
||||
|
||||
@@ -241,7 +242,7 @@
|
||||
for (const emp of employees) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/salary/${emp.employee_id}/current`
|
||||
`${API_BASE}/payroll/settings/salary/${emp.employee_id}/current`,
|
||||
);
|
||||
if (response.ok) {
|
||||
const setting = await response.json();
|
||||
@@ -255,7 +256,7 @@
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`従業員 ${emp.employee_id} の給与設定取得失敗`,
|
||||
error
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -316,7 +317,7 @@
|
||||
})" style="padding: 5px 10px; font-size: 12px;">編集</button>
|
||||
</td>
|
||||
</tr>
|
||||
`
|
||||
`,
|
||||
)
|
||||
.join("")
|
||||
: '<tr><td colspan="11" style="text-align:center;">給与設定が登録されていません</td></tr>'
|
||||
@@ -334,7 +335,7 @@
|
||||
// 全従業員の設定を取得して該当するものを探す
|
||||
for (const emp of employees) {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/salary/${emp.employee_id}`
|
||||
`${API_BASE}/payroll/settings/salary/${emp.employee_id}`,
|
||||
);
|
||||
const settings = await response.json();
|
||||
const setting = settings.find((s) => s.setting_id === settingId);
|
||||
@@ -408,7 +409,7 @@
|
||||
commute_allowance: parseFloat(formData.get("commute_allowance")) || 0,
|
||||
other_allowance: parseFloat(formData.get("other_allowance")) || 0,
|
||||
employment_insurance_eligible: document.getElementById(
|
||||
"employment_insurance_eligible"
|
||||
"employment_insurance_eligible",
|
||||
).checked,
|
||||
valid_from: formData.get("valid_from"),
|
||||
};
|
||||
@@ -431,7 +432,7 @@
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(data),
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
// 新規追加
|
||||
@@ -444,7 +445,7 @@
|
||||
|
||||
if (response.ok) {
|
||||
alert(
|
||||
settingId ? "給与設定を更新しました" : "給与設定を保存しました"
|
||||
settingId ? "給与設定を更新しました" : "給与設定を保存しました",
|
||||
);
|
||||
hideAddForm();
|
||||
loadAllSalarySettings();
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="js/auth.js"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>給与管理 - 設定</title>
|
||||
<link rel="stylesheet" href="/css/style.css" />
|
||||
@@ -651,7 +652,7 @@
|
||||
async function loadStdRemunerationYears() {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/standard-remuneration/years`
|
||||
`${API_BASE}/payroll/settings/standard-remuneration/years`,
|
||||
);
|
||||
const years = await response.json();
|
||||
|
||||
@@ -678,7 +679,7 @@
|
||||
<div id="std-table-${year}">読み込み中...</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
`,
|
||||
)
|
||||
.join("");
|
||||
|
||||
@@ -729,7 +730,7 @@
|
||||
|
||||
// セッションストレージからメタデータを取得
|
||||
const metadata = JSON.parse(
|
||||
sessionStorage.getItem("stdRemunMetadata") || "{}"
|
||||
sessionStorage.getItem("stdRemunMetadata") || "{}",
|
||||
);
|
||||
console.log("取得したメタデータ:", metadata);
|
||||
|
||||
@@ -752,9 +753,8 @@
|
||||
document.getElementById(`std-table-${year}`).innerHTML = tablesHtml;
|
||||
} catch (error) {
|
||||
console.error("データ読み込みエラー:", error);
|
||||
document.getElementById(
|
||||
`std-table-${year}`
|
||||
).innerHTML = `<p style="color:red;">データの取得に失敗しました: ${error.message}</p>`;
|
||||
document.getElementById(`std-table-${year}`).innerHTML =
|
||||
`<p style="color:red;">データの取得に失敗しました: ${error.message}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -768,7 +768,7 @@
|
||||
console.log("renderStdRemunerationTable - metadata内容:", metadata);
|
||||
console.log(
|
||||
"renderStdRemunerationTable - metadata.source:",
|
||||
metadata.source
|
||||
metadata.source,
|
||||
);
|
||||
if (metadata.source) {
|
||||
sourceHtml = `<p style="margin-bottom: 15px; padding: 10px; background: #f0f8ff; border-left: 3px solid #0066cc; color: #333;"><strong>対象期間:</strong> ${metadata.source}</p>`;
|
||||
@@ -831,7 +831,7 @@
|
||||
step1Data.map((d) => ({
|
||||
grade: d.grade,
|
||||
pension: d.pension_insurance,
|
||||
}))
|
||||
})),
|
||||
);
|
||||
|
||||
// ステップ2: 非ゼロ値を下方へ延続(前の有効値が0以下になったら、前の非ゼロ値を使用)
|
||||
@@ -856,7 +856,7 @@
|
||||
adjustedData.map((d) => ({
|
||||
grade: d.grade,
|
||||
pension: d.pension_insurance,
|
||||
}))
|
||||
})),
|
||||
);
|
||||
|
||||
console.log("調整後のデータ(最初の3件):", adjustedData.slice(0, 3));
|
||||
@@ -927,15 +927,15 @@
|
||||
<td>${Number(row.salary_from ?? 0).toLocaleString()}</td>
|
||||
<td>${Number(row.salary_to ?? 0).toLocaleString()}</td>
|
||||
<td>${Number(
|
||||
row.health_insurance_no_care ?? 0
|
||||
row.health_insurance_no_care ?? 0,
|
||||
).toLocaleString()}</td>
|
||||
<td>${Number(healthNoCareHalf ?? 0).toLocaleString()}</td>
|
||||
<td>${Number(
|
||||
row.health_insurance_with_care ?? 0
|
||||
row.health_insurance_with_care ?? 0,
|
||||
).toLocaleString()}</td>
|
||||
<td>${Number(healthWithCareHalf ?? 0).toLocaleString()}</td>
|
||||
<td>${Number(
|
||||
row.pension_insurance ?? 0
|
||||
row.pension_insurance ?? 0,
|
||||
).toLocaleString()}</td>
|
||||
<td>${Number(pensionHalf ?? 0).toLocaleString()}</td>
|
||||
</tr>
|
||||
@@ -958,7 +958,7 @@
|
||||
|
||||
async function deleteStdRemunerationYear(year) {
|
||||
const confirmed = confirm(
|
||||
`⚠️ 警告\n\n${year}年度の標準報酬月額表データを削除します。\nこの操作は取り消せません。\n\n本当に削除してもよろしいですか?`
|
||||
`⚠️ 警告\n\n${year}年度の標準報酬月額表データを削除します。\nこの操作は取り消せません。\n\n本当に削除してもよろしいですか?`,
|
||||
);
|
||||
if (!confirmed) {
|
||||
console.log(`${year}年度の削除がキャンセルされました`);
|
||||
@@ -970,7 +970,7 @@
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/standard-remuneration/year/${year}`,
|
||||
{ method: "DELETE" }
|
||||
{ method: "DELETE" },
|
||||
);
|
||||
|
||||
console.log("削除レスポンスステータス:", response.status);
|
||||
@@ -979,7 +979,7 @@
|
||||
|
||||
if (response.ok) {
|
||||
alert(
|
||||
`✓ ${year}年度のデータ(${result.deleted_count}件)を削除しました`
|
||||
`✓ ${year}年度のデータ(${result.deleted_count}件)を削除しました`,
|
||||
);
|
||||
console.log(`=== ${year}年度データ削除完了 ===`);
|
||||
|
||||
@@ -987,7 +987,7 @@
|
||||
await loadStdRemunerationYears();
|
||||
} else {
|
||||
alert(
|
||||
"削除に失敗しました:\n" + JSON.stringify(result.detail || result)
|
||||
"削除に失敗しました:\n" + JSON.stringify(result.detail || result),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -998,7 +998,7 @@
|
||||
|
||||
async function clearAllStdRemunerationData() {
|
||||
const confirmed = confirm(
|
||||
"⚠️ 警告\n\n標準報酬月額表のすべてのデータを削除します。\nこの操作は取り消せません。\n\n本当に削除してもよろしいですか?"
|
||||
"⚠️ 警告\n\n標準報酬月額表のすべてのデータを削除します。\nこの操作は取り消せません。\n\n本当に削除してもよろしいですか?",
|
||||
);
|
||||
if (!confirmed) {
|
||||
console.log("削除操作がキャンセルされました");
|
||||
@@ -1010,7 +1010,7 @@
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/standard-remuneration/all`,
|
||||
{ method: "DELETE" }
|
||||
{ method: "DELETE" },
|
||||
);
|
||||
|
||||
console.log("削除レスポンスステータス:", response.status);
|
||||
@@ -1019,7 +1019,7 @@
|
||||
|
||||
if (response.ok) {
|
||||
alert(
|
||||
`✓ ${result.deleted_count}件のデータを削除しました\n\n再度Excelファイルをインポートしてください`
|
||||
`✓ ${result.deleted_count}件のデータを削除しました\n\n再度Excelファイルをインポートしてください`,
|
||||
);
|
||||
console.log("=== 全データ削除完了 ===");
|
||||
|
||||
@@ -1028,7 +1028,7 @@
|
||||
"<p>データが削除されました。Excelファイルを再度インポートしてください。</p>";
|
||||
} else {
|
||||
alert(
|
||||
"削除に失敗しました:\n" + JSON.stringify(result.detail || result)
|
||||
"削除に失敗しました:\n" + JSON.stringify(result.detail || result),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -1150,7 +1150,7 @@
|
||||
async function editInsuranceRate(rateId) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/insurance-rates`
|
||||
`${API_BASE}/payroll/settings/insurance-rates`,
|
||||
);
|
||||
const rates = await response.json();
|
||||
const rate = rates.find((r) => r.rate_id === rateId);
|
||||
@@ -1184,7 +1184,7 @@
|
||||
async function copyInsuranceRate(rateId) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/insurance-rates`
|
||||
`${API_BASE}/payroll/settings/insurance-rates`,
|
||||
);
|
||||
const rates = await response.json();
|
||||
const rate = rates.find((r) => r.rate_id === rateId);
|
||||
@@ -1265,7 +1265,7 @@
|
||||
// 介護保険年齢を数値に変換(空の場合はnull)
|
||||
if (data.care_insurance_age_threshold) {
|
||||
data.care_insurance_age_threshold = parseInt(
|
||||
data.care_insurance_age_threshold
|
||||
data.care_insurance_age_threshold,
|
||||
);
|
||||
} else {
|
||||
data.care_insurance_age_threshold = null;
|
||||
@@ -1278,7 +1278,7 @@
|
||||
"送信するURL:",
|
||||
isEditMode
|
||||
? `${API_BASE}/payroll/settings/insurance-rates/${rateId}`
|
||||
: `${API_BASE}/payroll/settings/insurance-rates`
|
||||
: `${API_BASE}/payroll/settings/insurance-rates`,
|
||||
);
|
||||
console.log("送信するデータ:", JSON.stringify(data, null, 2));
|
||||
|
||||
@@ -1302,7 +1302,7 @@
|
||||
const result = await response.json();
|
||||
console.log("成功レスポンス:", result);
|
||||
alert(
|
||||
isEditMode ? "保険料率を更新しました" : "保険料率を登録しました"
|
||||
isEditMode ? "保険料率を更新しました" : "保険料率を登録しました",
|
||||
);
|
||||
hideInsuranceForm();
|
||||
loadInsuranceRates();
|
||||
@@ -1312,7 +1312,7 @@
|
||||
alert(
|
||||
(isEditMode ? "更新" : "登録") +
|
||||
"に失敗しました: " +
|
||||
JSON.stringify(error.detail)
|
||||
JSON.stringify(error.detail),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -1328,7 +1328,7 @@
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/insurance-rates/${rateId}`,
|
||||
{ method: "DELETE" }
|
||||
{ method: "DELETE" },
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
@@ -1362,7 +1362,7 @@
|
||||
async function loadChildSupportRates() {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/child-support-rates`
|
||||
`${API_BASE}/payroll/settings/child-support-rates`,
|
||||
);
|
||||
const rates = await response.json();
|
||||
|
||||
@@ -1390,10 +1390,10 @@
|
||||
<tr>
|
||||
<td>${rate.rate_year}</td>
|
||||
<td>¥${Number(
|
||||
rate.income_threshold
|
||||
rate.income_threshold,
|
||||
).toLocaleString()}</td>
|
||||
<td>${(parseFloat(rate.contribution_rate) * 100).toFixed(
|
||||
3
|
||||
3,
|
||||
)}%</td>
|
||||
<td>${rate.notes || "-"}</td>
|
||||
<td>
|
||||
@@ -1405,7 +1405,7 @@
|
||||
})">削除</button>
|
||||
</td>
|
||||
</tr>
|
||||
`
|
||||
`,
|
||||
)
|
||||
.join("")}
|
||||
</tbody>
|
||||
@@ -1422,7 +1422,7 @@
|
||||
async function editChildSupportRate(contributionId) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/child-support-rates`
|
||||
`${API_BASE}/payroll/settings/child-support-rates`,
|
||||
);
|
||||
const rates = await response.json();
|
||||
const rate = rates.find((r) => r.contribution_id === contributionId);
|
||||
@@ -1493,7 +1493,7 @@
|
||||
alert(
|
||||
isEditMode
|
||||
? "子ども・子育て拠出金率を更新しました"
|
||||
: "子ども・子育て拠出金率を登録しました"
|
||||
: "子ども・子育て拠出金率を登録しました",
|
||||
);
|
||||
hideChildSupportForm();
|
||||
loadChildSupportRates();
|
||||
@@ -1502,7 +1502,7 @@
|
||||
alert(
|
||||
(isEditMode ? "更新" : "登録") +
|
||||
"に失敗しました: " +
|
||||
JSON.stringify(error.detail)
|
||||
JSON.stringify(error.detail),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -1517,7 +1517,7 @@
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/child-support-rates/${contributionId}`,
|
||||
{ method: "DELETE" }
|
||||
{ method: "DELETE" },
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
@@ -1550,7 +1550,7 @@
|
||||
async function loadLimitSettings() {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/insurance-limits`
|
||||
`${API_BASE}/payroll/settings/insurance-limits`,
|
||||
);
|
||||
const limits = await response.json();
|
||||
|
||||
@@ -1579,8 +1579,8 @@
|
||||
<td>${limit.setting_type}</td>
|
||||
<td>¥${Number(limit.limit_amount).toLocaleString()}</td>
|
||||
<td>${limit.valid_from} ~ ${
|
||||
limit.valid_to || "現在"
|
||||
}</td>
|
||||
limit.valid_to || "現在"
|
||||
}</td>
|
||||
<td>${limit.notes || "-"}</td>
|
||||
<td>
|
||||
<button class="btn btn-primary btn-small" onclick="editLimitSetting(${
|
||||
@@ -1591,7 +1591,7 @@
|
||||
})">削除</button>
|
||||
</td>
|
||||
</tr>
|
||||
`
|
||||
`,
|
||||
)
|
||||
.join("")}
|
||||
</tbody>
|
||||
@@ -1608,7 +1608,7 @@
|
||||
async function editLimitSetting(limitId) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/insurance-limits`
|
||||
`${API_BASE}/payroll/settings/insurance-limits`,
|
||||
);
|
||||
const limits = await response.json();
|
||||
const limit = limits.find((l) => l.limit_id === limitId);
|
||||
@@ -1676,7 +1676,7 @@
|
||||
|
||||
if (response.ok) {
|
||||
alert(
|
||||
isEditMode ? "上限設定を更新しました" : "上限設定を登録しました"
|
||||
isEditMode ? "上限設定を更新しました" : "上限設定を登録しました",
|
||||
);
|
||||
hideLimitForm();
|
||||
loadLimitSettings();
|
||||
@@ -1685,7 +1685,7 @@
|
||||
alert(
|
||||
(isEditMode ? "更新" : "登録") +
|
||||
"に失敗しました: " +
|
||||
JSON.stringify(error.detail)
|
||||
JSON.stringify(error.detail),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -1700,7 +1700,7 @@
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/insurance-limits/${limitId}`,
|
||||
{ method: "DELETE" }
|
||||
{ method: "DELETE" },
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
@@ -1732,7 +1732,7 @@
|
||||
async function loadIncomeTaxYears() {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/income-tax/years`
|
||||
`${API_BASE}/payroll/settings/income-tax/years`,
|
||||
);
|
||||
const data = await response.json();
|
||||
|
||||
@@ -1772,7 +1772,7 @@
|
||||
<div id="table-${year}">読み込み中...</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
`,
|
||||
)
|
||||
.join("");
|
||||
|
||||
@@ -1785,7 +1785,7 @@
|
||||
|
||||
async function deleteTaxDataYear(year) {
|
||||
const confirmed = confirm(
|
||||
`⚠️ 警告\n\n${year}年度の所得税率表データを削除します。\nこの操作は取り消せません。\n\n本当に削除してもよろしいですか?`
|
||||
`⚠️ 警告\n\n${year}年度の所得税率表データを削除します。\nこの操作は取り消せません。\n\n本当に削除してもよろしいですか?`,
|
||||
);
|
||||
if (!confirmed) {
|
||||
console.log(`${year}年度の削除がキャンセルされました`);
|
||||
@@ -1797,7 +1797,7 @@
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/income-tax/year/${year}`,
|
||||
{ method: "DELETE" }
|
||||
{ method: "DELETE" },
|
||||
);
|
||||
|
||||
console.log("削除レスポンスステータス:", response.status);
|
||||
@@ -1806,7 +1806,7 @@
|
||||
|
||||
if (response.ok) {
|
||||
alert(
|
||||
`✓ ${year}年度のデータ(${result.deleted_count}件)を削除しました`
|
||||
`✓ ${year}年度のデータ(${result.deleted_count}件)を削除しました`,
|
||||
);
|
||||
console.log(`=== ${year}年度所得税データ削除完了 ===`);
|
||||
|
||||
@@ -1814,7 +1814,7 @@
|
||||
await loadIncomeTaxYears();
|
||||
} else {
|
||||
alert(
|
||||
"削除に失敗しました:\n" + JSON.stringify(result.detail || result)
|
||||
"削除に失敗しました:\n" + JSON.stringify(result.detail || result),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -1851,7 +1851,7 @@
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/settings/income-tax?${params}`
|
||||
`${API_BASE}/payroll/settings/income-tax?${params}`,
|
||||
);
|
||||
const taxes = await response.json();
|
||||
|
||||
@@ -1879,13 +1879,13 @@
|
||||
<tr>
|
||||
<td>${tax.dependents_count}人</td>
|
||||
<td>¥${Number(
|
||||
tax.monthly_income_from
|
||||
tax.monthly_income_from,
|
||||
).toLocaleString()}</td>
|
||||
<td>¥${Number(tax.monthly_income_to).toLocaleString()}</td>
|
||||
<td>¥${Number(tax.tax_amount).toLocaleString()}</td>
|
||||
<td>${tax.valid_from} ~ ${tax.valid_to || "現在"}</td>
|
||||
</tr>
|
||||
`
|
||||
`,
|
||||
)
|
||||
.join("")}
|
||||
</tbody>
|
||||
@@ -1908,7 +1908,7 @@
|
||||
|
||||
const taxYear = prompt(
|
||||
"税年度を入力してください(空欄の場合はファイルから自動抽出):",
|
||||
""
|
||||
"",
|
||||
);
|
||||
|
||||
try {
|
||||
@@ -1948,7 +1948,7 @@
|
||||
// 都道府県をプロンプトで取得
|
||||
const prefecture = prompt(
|
||||
"都道府県を入力してください(例: 東京、神奈川):",
|
||||
""
|
||||
"",
|
||||
);
|
||||
if (!prefecture || !prefecture.trim()) {
|
||||
alert("都道府県を入力してください");
|
||||
@@ -1970,7 +1970,7 @@
|
||||
{
|
||||
method: "POST",
|
||||
body: formData,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
console.log("レスポンスステータス:", response.status);
|
||||
@@ -1990,7 +1990,7 @@
|
||||
console.log("メタデータ保存:", result.metadata);
|
||||
sessionStorage.setItem(
|
||||
"stdRemunMetadata",
|
||||
JSON.stringify(result.metadata)
|
||||
JSON.stringify(result.metadata),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2009,7 +2009,7 @@
|
||||
} else if (typeof errorDetail === "object") {
|
||||
errorMessage += JSON.stringify(errorDetail, null, 2).substring(
|
||||
0,
|
||||
1000
|
||||
1000,
|
||||
);
|
||||
} else {
|
||||
errorMessage += String(errorDetail).substring(0, 500);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="js/auth.js"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>給与管理システム</title>
|
||||
<link rel="stylesheet" href="/css/style.css" />
|
||||
@@ -60,7 +61,10 @@
|
||||
<p>従業員の給与計算、扶養管理、保険料・税金の管理を行います</p>
|
||||
|
||||
<div class="menu-grid">
|
||||
<div class="menu-card" onclick="location.href='payroll-employees.html'">
|
||||
<div
|
||||
class="menu-card"
|
||||
onclick="location.href = 'payroll-employees.html'"
|
||||
>
|
||||
<div class="icon">👥</div>
|
||||
<h2>従業員管理</h2>
|
||||
<p>従業員の基本情報、扶養家族情報を管理します</p>
|
||||
@@ -68,7 +72,7 @@
|
||||
|
||||
<div
|
||||
class="menu-card"
|
||||
onclick="location.href='payroll-salary-settings.html'"
|
||||
onclick="location.href = 'payroll-salary-settings.html'"
|
||||
>
|
||||
<div class="icon">💰</div>
|
||||
<h2>給与設定</h2>
|
||||
@@ -77,14 +81,17 @@
|
||||
|
||||
<div
|
||||
class="menu-card"
|
||||
onclick="location.href='payroll-calculation.html'"
|
||||
onclick="location.href = 'payroll-calculation.html'"
|
||||
>
|
||||
<div class="icon">🧮</div>
|
||||
<h2>月次給与計算</h2>
|
||||
<p>毎月の給与計算、給与明細の作成・承認を行います</p>
|
||||
</div>
|
||||
|
||||
<div class="menu-card" onclick="location.href='payroll-settings.html'">
|
||||
<div
|
||||
class="menu-card"
|
||||
onclick="location.href = 'payroll-settings.html'"
|
||||
>
|
||||
<div class="icon">⚙️</div>
|
||||
<h2>税率・保険料設定</h2>
|
||||
<p>社会保険料率、所得税率表の管理を行います</p>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="js/auth.js"></script>
|
||||
<title>試算表</title>
|
||||
<style>
|
||||
body {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="js/auth.js"></script>
|
||||
<title>账票出力 - UI テスト</title>
|
||||
<style>
|
||||
body {
|
||||
|
||||
Reference in New Issue
Block a user