修正
This commit is contained in:
@@ -300,7 +300,7 @@ class PayrollCalculationService:
|
|||||||
# まず指定年度の標準報酬月額表を検索
|
# まず指定年度の標準報酬月額表を検索
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
SELECT monthly_amount
|
SELECT monthly_amount, child_support
|
||||||
FROM standard_remuneration
|
FROM standard_remuneration
|
||||||
WHERE rate_year = %s
|
WHERE rate_year = %s
|
||||||
AND salary_from <= %s
|
AND salary_from <= %s
|
||||||
@@ -318,7 +318,7 @@ class PayrollCalculationService:
|
|||||||
previous_year = target_year - year_offset
|
previous_year = target_year - year_offset
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
SELECT monthly_amount
|
SELECT monthly_amount, child_support
|
||||||
FROM standard_remuneration
|
FROM standard_remuneration
|
||||||
WHERE rate_year = %s
|
WHERE rate_year = %s
|
||||||
AND salary_from <= %s
|
AND salary_from <= %s
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ja">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<title>资金流水</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>📄 资金流水</h2>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label>账户ID:</label>
|
|
||||||
<input id="accountId" type="number" value="101" style="width: 80px" />
|
|
||||||
<label>从:</label>
|
|
||||||
<input id="fromDate" type="date" />
|
|
||||||
<label>到:</label>
|
|
||||||
<input id="toDate" type="date" />
|
|
||||||
<button onclick="loadTransactions()">查询</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table
|
|
||||||
border="1"
|
|
||||||
cellpadding="6"
|
|
||||||
style="margin-top: 10px; border-collapse: collapse"
|
|
||||||
>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>日期</th>
|
|
||||||
<th>摘要</th>
|
|
||||||
<th style="text-align: right">金额</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="txBody">
|
|
||||||
<tr>
|
|
||||||
<td colspan="3">请输入条件后查询</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function formatYen(v) {
|
|
||||||
return "¥ " + Number(v).toLocaleString("ja-JP");
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadTransactions() {
|
|
||||||
const accountId = document.getElementById("accountId").value;
|
|
||||||
const from = document.getElementById("fromDate").value;
|
|
||||||
const to = document.getElementById("toDate").value;
|
|
||||||
|
|
||||||
let url = `/cash/transactions?account_id=${accountId}`;
|
|
||||||
if (from) url += `&from=${from}`;
|
|
||||||
if (to) url += `&to=${to}`;
|
|
||||||
|
|
||||||
const body = document.getElementById("txBody");
|
|
||||||
body.innerHTML = "<tr><td colspan='3'>读取中...</td></tr>";
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch(url);
|
|
||||||
const data = await res.json();
|
|
||||||
|
|
||||||
body.innerHTML = "";
|
|
||||||
if (data.length === 0) {
|
|
||||||
body.innerHTML = "<tr><td colspan='3'>无数据</td></tr>";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.forEach((row) => {
|
|
||||||
const tr = document.createElement("tr");
|
|
||||||
tr.innerHTML = `
|
|
||||||
<td>${row.journal_date}</td>
|
|
||||||
<td>${row.description}</td>
|
|
||||||
<td style="text-align:right;color:${row.amount < 0 ? "red" : "black"};">
|
|
||||||
${formatYen(row.amount)}
|
|
||||||
</td>
|
|
||||||
`;
|
|
||||||
body.appendChild(tr);
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
body.innerHTML = "<tr><td colspan='3'>读取失败</td></tr>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getQueryParam(name) {
|
|
||||||
const params = new URLSearchParams(window.location.search);
|
|
||||||
return params.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = () => {
|
|
||||||
const accountId = getQueryParam("account_id");
|
|
||||||
if (accountId) {
|
|
||||||
document.getElementById("accountId").value = accountId;
|
|
||||||
loadTransactions();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="ja">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<title>资金余额</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>💰 当前资金状况</h2>
|
|
||||||
|
|
||||||
<table border="1" cellpadding="6" style="border-collapse: collapse">
|
|
||||||
<thead>
|
|
||||||
<tr style="background: #f5f5f5">
|
|
||||||
<th>账户</th>
|
|
||||||
<th style="text-align: right">余额</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="cashBalanceBody">
|
|
||||||
<tr>
|
|
||||||
<td colspan="2">读取中...</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
<tr style="font-weight: bold">
|
|
||||||
<td>合计</td>
|
|
||||||
<td id="cashBalanceTotal" style="text-align: right">-</td>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const API_BASE = "";
|
|
||||||
|
|
||||||
function formatYen(v) {
|
|
||||||
return "¥ " + Number(v).toLocaleString("ja-JP");
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadCashBalance() {
|
|
||||||
const body = document.getElementById("cashBalanceBody");
|
|
||||||
const totalEl = document.getElementById("cashBalanceTotal");
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await fetch("/cash/balance");
|
|
||||||
const data = await res.json();
|
|
||||||
|
|
||||||
body.innerHTML = "";
|
|
||||||
|
|
||||||
data.items.forEach((item) => {
|
|
||||||
const tr = document.createElement("tr");
|
|
||||||
|
|
||||||
tr.style.cursor = "pointer";
|
|
||||||
tr.onclick = () => {
|
|
||||||
window.location.href = `/cash-transactions.html?account_id=${item.account_id}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
tr.innerHTML = `
|
|
||||||
<td style="color:#007acc; text-decoration:underline;">
|
|
||||||
${item.account_name}
|
|
||||||
</td>
|
|
||||||
<td style="text-align:right;">
|
|
||||||
${formatYen(item.balance)}
|
|
||||||
</td>
|
|
||||||
`;
|
|
||||||
|
|
||||||
tbody.appendChild(tr);
|
|
||||||
});
|
|
||||||
|
|
||||||
totalEl.innerText = formatYen(data.total_balance);
|
|
||||||
} catch (e) {
|
|
||||||
body.innerHTML = `<tr><td colspan="2">读取失败</td></tr>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("DOMContentLoaded", loadCashBalance);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,341 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="ja">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>給与管理 - 従業員管理</title>
|
|
||||||
<link rel="stylesheet" href="/css/style.css" />
|
|
||||||
<style>
|
|
||||||
.nav-tabs {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
border-bottom: 2px solid #ddd;
|
|
||||||
}
|
|
||||||
.nav-tab {
|
|
||||||
padding: 10px 20px;
|
|
||||||
cursor: pointer;
|
|
||||||
border: none;
|
|
||||||
background: #f0f0f0;
|
|
||||||
border-radius: 5px 5px 0 0;
|
|
||||||
}
|
|
||||||
.nav-tab.active {
|
|
||||||
background: #007bff;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.tab-content {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.tab-content.active {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.employee-list {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
.employee-item {
|
|
||||||
padding: 15px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.employee-item:hover {
|
|
||||||
background: #f9f9f9;
|
|
||||||
}
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
.form-group label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.form-group input,
|
|
||||||
.form-group select,
|
|
||||||
.form-group textarea {
|
|
||||||
width: 100%;
|
|
||||||
padding: 8px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.btn {
|
|
||||||
padding: 10px 20px;
|
|
||||||
margin-right: 10px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.btn-primary {
|
|
||||||
background: #007bff;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.btn-secondary {
|
|
||||||
background: #6c757d;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.btn-danger {
|
|
||||||
background: #dc3545;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.dependent-list {
|
|
||||||
margin-top: 20px;
|
|
||||||
padding: 15px;
|
|
||||||
background: #f9f9f9;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
.dependent-item {
|
|
||||||
padding: 10px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
background: white;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1>給与管理 - 従業員管理</h1>
|
|
||||||
|
|
||||||
<div class="nav-tabs">
|
|
||||||
<button class="nav-tab active" onclick="showTab('list')">
|
|
||||||
従業員一覧
|
|
||||||
</button>
|
|
||||||
<button class="nav-tab" onclick="showTab('create')">新規登録</button>
|
|
||||||
<button
|
|
||||||
class="nav-tab"
|
|
||||||
onclick="showTab('detail')"
|
|
||||||
id="detailTab"
|
|
||||||
style="display: none"
|
|
||||||
>
|
|
||||||
従業員詳細
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 従業員一覧タブ -->
|
|
||||||
<div id="tab-list" class="tab-content active">
|
|
||||||
<div class="filters">
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="filterActive"
|
|
||||||
checked
|
|
||||||
onchange="loadEmployees()"
|
|
||||||
/>
|
|
||||||
在職中のみ表示
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div id="employeeList" class="employee-list"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 新規登録タブ -->
|
|
||||||
<div id="tab-create" class="tab-content">
|
|
||||||
<h2>従業員の新規登録</h2>
|
|
||||||
<form id="createForm" onsubmit="createEmployee(event)">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>従業員コード *</label>
|
|
||||||
<input type="text" name="employee_code" required />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>氏名 *</label>
|
|
||||||
<input type="text" name="name" required />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>フリガナ</label>
|
|
||||||
<input type="text" name="name_kana" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>メールアドレス</label>
|
|
||||||
<input type="email" name="email" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>電話番号</label>
|
|
||||||
<input type="tel" name="phone" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>入社日 *</label>
|
|
||||||
<input type="date" name="hire_date" required />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>銀行名</label>
|
|
||||||
<input type="text" name="bank_name" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>支店名</label>
|
|
||||||
<input type="text" name="bank_branch" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>口座番号</label>
|
|
||||||
<input type="text" name="bank_account_number" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>口座種別</label>
|
|
||||||
<select name="bank_account_type">
|
|
||||||
<option value="">選択してください</option>
|
|
||||||
<option value="普通">普通</option>
|
|
||||||
<option value="当座">当座</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>備考</label>
|
|
||||||
<textarea name="notes" rows="3"></textarea>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary">登録</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-secondary"
|
|
||||||
onclick="showTab('list')"
|
|
||||||
>
|
|
||||||
キャンセル
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 従業員詳細タブ -->
|
|
||||||
<div id="tab-detail" class="tab-content">
|
|
||||||
<div id="employeeDetail"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const API_BASE = "";
|
|
||||||
let currentEmployee = null;
|
|
||||||
|
|
||||||
function showTab(tabName) {
|
|
||||||
document
|
|
||||||
.querySelectorAll(".nav-tab")
|
|
||||||
.forEach((tab) => tab.classList.remove("active"));
|
|
||||||
document
|
|
||||||
.querySelectorAll(".tab-content")
|
|
||||||
.forEach((content) => content.classList.remove("active"));
|
|
||||||
|
|
||||||
if (tabName === "list") {
|
|
||||||
document.querySelector(".nav-tab").classList.add("active");
|
|
||||||
document.getElementById("tab-list").classList.add("active");
|
|
||||||
loadEmployees();
|
|
||||||
} else if (tabName === "create") {
|
|
||||||
document.querySelectorAll(".nav-tab")[1].classList.add("active");
|
|
||||||
document.getElementById("tab-create").classList.add("active");
|
|
||||||
} else if (tabName === "detail") {
|
|
||||||
document.getElementById("detailTab").classList.add("active");
|
|
||||||
document.getElementById("tab-detail").classList.add("active");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadEmployees() {
|
|
||||||
const isActive = document.getElementById("filterActive").checked;
|
|
||||||
const url = `${API_BASE}/payroll/employees/${isActive ? "?is_active=true" : ""}`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(url);
|
|
||||||
const employees = await response.json();
|
|
||||||
|
|
||||||
const html = employees
|
|
||||||
.map(
|
|
||||||
(emp) => `
|
|
||||||
<div class="employee-item" onclick="viewEmployee(${emp.employee_id})">
|
|
||||||
<strong>${emp.employee_code}</strong> - ${emp.name}
|
|
||||||
${emp.name_kana ? `(${emp.name_kana})` : ""}
|
|
||||||
<br>
|
|
||||||
<small>入社日: ${emp.hire_date} ${emp.termination_date ? ` | 退職日: ${emp.termination_date}` : ""}</small>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.join("");
|
|
||||||
|
|
||||||
document.getElementById("employeeList").innerHTML =
|
|
||||||
html || "<p>従業員が登録されていません</p>";
|
|
||||||
} catch (error) {
|
|
||||||
alert("従業員一覧の取得に失敗しました");
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createEmployee(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const form = event.target;
|
|
||||||
const formData = new FormData(form);
|
|
||||||
const data = Object.fromEntries(formData.entries());
|
|
||||||
|
|
||||||
// 空の値を除外
|
|
||||||
Object.keys(data).forEach((key) => {
|
|
||||||
if (data[key] === "") {
|
|
||||||
delete data[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${API_BASE}/payroll/employees/`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
alert("従業員を登録しました");
|
|
||||||
form.reset();
|
|
||||||
showTab("list");
|
|
||||||
} else {
|
|
||||||
const error = await response.json();
|
|
||||||
alert("登録に失敗しました: " + error.detail);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
alert("登録に失敗しました");
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function viewEmployee(employeeId) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
`${API_BASE}/payroll/employees/${employeeId}`,
|
|
||||||
);
|
|
||||||
const employee = await response.json();
|
|
||||||
currentEmployee = employee;
|
|
||||||
|
|
||||||
const html = `
|
|
||||||
<h2>${employee.name} (${employee.employee_code})</h2>
|
|
||||||
<p><strong>フリガナ:</strong> ${employee.name_kana || "-"}</p>
|
|
||||||
<p><strong>メール:</strong> ${employee.email || "-"}</p>
|
|
||||||
<p><strong>電話:</strong> ${employee.phone || "-"}</p>
|
|
||||||
<p><strong>入社日:</strong> ${employee.hire_date}</p>
|
|
||||||
<p><strong>退職日:</strong> ${employee.termination_date || "-"}</p>
|
|
||||||
<p><strong>銀行:</strong> ${employee.bank_name || "-"} ${employee.bank_branch || ""}</p>
|
|
||||||
<p><strong>口座:</strong> ${employee.bank_account_type || ""} ${employee.bank_account_number || "-"}</p>
|
|
||||||
<p><strong>備考:</strong> ${employee.notes || "-"}</p>
|
|
||||||
|
|
||||||
<div class="dependent-list">
|
|
||||||
<h3>扶養家族</h3>
|
|
||||||
${
|
|
||||||
employee.dependents
|
|
||||||
.map(
|
|
||||||
(dep) => `
|
|
||||||
<div class="dependent-item">
|
|
||||||
<strong>${dep.name}</strong> (${dep.relationship})
|
|
||||||
${dep.birth_date ? ` - 生年月日: ${dep.birth_date}` : ""}
|
|
||||||
${dep.is_spouse ? ' <span style="color:blue;">[配偶者]</span>' : ""}
|
|
||||||
${dep.is_disabled ? ' <span style="color:red;">[障害者]</span>' : ""}
|
|
||||||
<br><small>有効期間: ${dep.valid_from} ~ ${dep.valid_to || "現在"}</small>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.join("") || "<p>扶養家族が登録されていません</p>"
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="btn btn-secondary" onclick="showTab('list')">戻る</button>
|
|
||||||
`;
|
|
||||||
|
|
||||||
document.getElementById("employeeDetail").innerHTML = html;
|
|
||||||
document.getElementById("detailTab").style.display = "block";
|
|
||||||
showTab("detail");
|
|
||||||
} catch (error) {
|
|
||||||
alert("従業員情報の取得に失敗しました");
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初期化
|
|
||||||
loadEmployees();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,348 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="ja">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>給与管理 - 給与設定</title>
|
|
||||||
<link rel="stylesheet" href="/css/style.css" />
|
|
||||||
<style>
|
|
||||||
.employee-select {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
padding: 15px;
|
|
||||||
background: #f9f9f9;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
.form-group label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.form-group input,
|
|
||||||
.form-group select {
|
|
||||||
width: 100%;
|
|
||||||
padding: 8px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.btn {
|
|
||||||
padding: 10px 20px;
|
|
||||||
margin-right: 10px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.btn-primary {
|
|
||||||
background: #007bff;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.btn-secondary {
|
|
||||||
background: #6c757d;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
table th,
|
|
||||||
table td {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
padding: 10px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
table th {
|
|
||||||
background: #f0f0f0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1>給与管理 - 給与設定</h1>
|
|
||||||
|
|
||||||
<div class="employee-select">
|
|
||||||
<label><strong>従業員を選択:</strong></label>
|
|
||||||
<select id="employeeSelect" onchange="loadSalarySettings()">
|
|
||||||
<option value="">従業員を選択してください</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="settingsContent" style="display: none">
|
|
||||||
<h2>給与設定履歴</h2>
|
|
||||||
<button class="btn btn-primary" onclick="showAddForm()">
|
|
||||||
新規設定追加
|
|
||||||
</button>
|
|
||||||
<div id="settingsList"></div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
id="addForm"
|
|
||||||
style="
|
|
||||||
display: none;
|
|
||||||
margin-top: 20px;
|
|
||||||
padding: 20px;
|
|
||||||
border: 2px solid #007bff;
|
|
||||||
border-radius: 5px;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<h3>給与設定の追加</h3>
|
|
||||||
<form onsubmit="saveSalarySetting(event)">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>基本給 (円) *</label>
|
|
||||||
<input type="number" name="base_salary" step="0.01" required />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>時給 (円)</label>
|
|
||||||
<input type="number" name="hourly_rate" step="0.01" />
|
|
||||||
<small>時給制の場合に入力してください</small>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>雇用形態 *</label>
|
|
||||||
<select name="employment_type" required>
|
|
||||||
<option value="">選択してください</option>
|
|
||||||
<option value="正社員">正社員</option>
|
|
||||||
<option value="契約社員">契約社員</option>
|
|
||||||
<option value="パート">パート</option>
|
|
||||||
<option value="アルバイト">アルバイト</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>支給形態 *</label>
|
|
||||||
<select name="payment_type" required>
|
|
||||||
<option value="">選択してください</option>
|
|
||||||
<option value="月給">月給</option>
|
|
||||||
<option value="時給">時給</option>
|
|
||||||
<option value="日給">日給</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>通勤手当 (円)</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
name="commute_allowance"
|
|
||||||
step="0.01"
|
|
||||||
value="0"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>その他手当 (円)</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
name="other_allowance"
|
|
||||||
step="0.01"
|
|
||||||
value="0"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>適用開始日 *</label>
|
|
||||||
<input type="date" name="valid_from" required />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>適用終了日</label>
|
|
||||||
<input type="date" name="valid_to" />
|
|
||||||
<small>通常は空欄のままにしてください</small>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary">保存</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-secondary"
|
|
||||||
onclick="hideAddForm()"
|
|
||||||
>
|
|
||||||
キャンセル
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
id="currentSetting"
|
|
||||||
style="
|
|
||||||
margin-top: 30px;
|
|
||||||
padding: 20px;
|
|
||||||
background: #e7f3ff;
|
|
||||||
border-radius: 5px;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<h3>現在の給与設定</h3>
|
|
||||||
<div id="currentSettingContent"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const API_BASE = "";
|
|
||||||
let currentEmployeeId = null;
|
|
||||||
|
|
||||||
async function loadEmployees() {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
`${API_BASE}/payroll/employees/?is_active=true`,
|
|
||||||
);
|
|
||||||
const employees = await response.json();
|
|
||||||
|
|
||||||
const select = document.getElementById("employeeSelect");
|
|
||||||
select.innerHTML =
|
|
||||||
'<option value="">従業員を選択してください</option>' +
|
|
||||||
employees
|
|
||||||
.map(
|
|
||||||
(emp) =>
|
|
||||||
`<option value="${emp.employee_id}">${emp.employee_code} - ${emp.name}</option>`,
|
|
||||||
)
|
|
||||||
.join("");
|
|
||||||
} catch (error) {
|
|
||||||
alert("従業員一覧の取得に失敗しました");
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadSalarySettings() {
|
|
||||||
const employeeId = document.getElementById("employeeSelect").value;
|
|
||||||
if (!employeeId) {
|
|
||||||
document.getElementById("settingsContent").style.display = "none";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentEmployeeId = employeeId;
|
|
||||||
document.getElementById("settingsContent").style.display = "block";
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 給与設定履歴を取得
|
|
||||||
const response = await fetch(
|
|
||||||
`${API_BASE}/payroll/settings/salary/${employeeId}`,
|
|
||||||
);
|
|
||||||
const settings = await response.json();
|
|
||||||
|
|
||||||
const html = `
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>適用期間</th>
|
|
||||||
<th>基本給</th>
|
|
||||||
<th>時給</th>
|
|
||||||
<th>雇用形態</th>
|
|
||||||
<th>支給形態</th>
|
|
||||||
<th>通勤手当</th>
|
|
||||||
<th>その他手当</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
${settings
|
|
||||||
.map(
|
|
||||||
(s) => `
|
|
||||||
<tr>
|
|
||||||
<td>${s.valid_from} ~ ${s.valid_to || "現在"}</td>
|
|
||||||
<td>¥${Number(s.base_salary).toLocaleString()}</td>
|
|
||||||
<td>${s.hourly_rate ? "¥" + Number(s.hourly_rate).toLocaleString() : "-"}</td>
|
|
||||||
<td>${s.employment_type}</td>
|
|
||||||
<td>${s.payment_type}</td>
|
|
||||||
<td>¥${Number(s.commute_allowance).toLocaleString()}</td>
|
|
||||||
<td>¥${Number(s.other_allowance).toLocaleString()}</td>
|
|
||||||
</tr>
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.join("")}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
`;
|
|
||||||
|
|
||||||
document.getElementById("settingsList").innerHTML = html;
|
|
||||||
|
|
||||||
// 現在の設定を取得
|
|
||||||
loadCurrentSetting(employeeId);
|
|
||||||
} catch (error) {
|
|
||||||
alert("給与設定の取得に失敗しました");
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadCurrentSetting(employeeId) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
`${API_BASE}/payroll/settings/salary/${employeeId}/current`,
|
|
||||||
);
|
|
||||||
if (response.ok) {
|
|
||||||
const setting = await response.json();
|
|
||||||
|
|
||||||
const html = `
|
|
||||||
<div style="display:grid; grid-template-columns: repeat(2, 1fr); gap:15px;">
|
|
||||||
<div><strong>基本給:</strong> ¥${Number(setting.base_salary).toLocaleString()}</div>
|
|
||||||
<div><strong>時給:</strong> ${setting.hourly_rate ? "¥" + Number(setting.hourly_rate).toLocaleString() : "-"}</div>
|
|
||||||
<div><strong>雇用形態:</strong> ${setting.employment_type}</div>
|
|
||||||
<div><strong>支給形態:</strong> ${setting.payment_type}</div>
|
|
||||||
<div><strong>通勤手当:</strong> ¥${Number(setting.commute_allowance).toLocaleString()}</div>
|
|
||||||
<div><strong>その他手当:</strong> ¥${Number(setting.other_allowance).toLocaleString()}</div>
|
|
||||||
<div><strong>適用期間:</strong> ${setting.valid_from} ~ ${setting.valid_to || "現在"}</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
document.getElementById("currentSettingContent").innerHTML = html;
|
|
||||||
} else {
|
|
||||||
document.getElementById("currentSettingContent").innerHTML =
|
|
||||||
"<p>給与設定が登録されていません</p>";
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
document.getElementById("currentSettingContent").innerHTML =
|
|
||||||
"<p>給与設定の取得に失敗しました</p>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showAddForm() {
|
|
||||||
document.getElementById("addForm").style.display = "block";
|
|
||||||
// デフォルト値設定
|
|
||||||
document.querySelector('[name="valid_from"]').value = new Date()
|
|
||||||
.toISOString()
|
|
||||||
.split("T")[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideAddForm() {
|
|
||||||
document.getElementById("addForm").style.display = "none";
|
|
||||||
document.querySelector("#addForm form").reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveSalarySetting(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const form = event.target;
|
|
||||||
const formData = new FormData(form);
|
|
||||||
const data = Object.fromEntries(formData.entries());
|
|
||||||
|
|
||||||
data.employee_id = parseInt(currentEmployeeId);
|
|
||||||
data.base_salary = parseFloat(data.base_salary) || 0;
|
|
||||||
data.commute_allowance = parseFloat(data.commute_allowance) || 0;
|
|
||||||
data.other_allowance = parseFloat(data.other_allowance) || 0;
|
|
||||||
|
|
||||||
if (data.hourly_rate) {
|
|
||||||
data.hourly_rate = parseFloat(data.hourly_rate);
|
|
||||||
} else {
|
|
||||||
delete data.hourly_rate;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data.valid_to) {
|
|
||||||
delete data.valid_to;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${API_BASE}/payroll/settings/salary`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
alert("給与設定を保存しました");
|
|
||||||
hideAddForm();
|
|
||||||
loadSalarySettings();
|
|
||||||
} else {
|
|
||||||
const error = await response.json();
|
|
||||||
alert("保存に失敗しました: " + error.detail);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
alert("保存に失敗しました");
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初期化
|
|
||||||
loadEmployees();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,135 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="ja">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>給与管理システム</title>
|
|
||||||
<link rel="stylesheet" href="/css/style.css" />
|
|
||||||
<style>
|
|
||||||
.menu-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
||||||
gap: 20px;
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
.menu-card {
|
|
||||||
padding: 30px;
|
|
||||||
border: 2px solid #007bff;
|
|
||||||
border-radius: 10px;
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
.menu-card:hover {
|
|
||||||
background: #007bff;
|
|
||||||
color: white;
|
|
||||||
transform: translateY(-5px);
|
|
||||||
box-shadow: 0 5px 15px rgba(0, 123, 255, 0.3);
|
|
||||||
}
|
|
||||||
.menu-card h2 {
|
|
||||||
margin: 0 0 15px 0;
|
|
||||||
font-size: 1.5em;
|
|
||||||
}
|
|
||||||
.menu-card p {
|
|
||||||
margin: 0;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
.menu-card:hover p {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
.icon {
|
|
||||||
font-size: 3em;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
.back-link {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<button
|
|
||||||
onclick="location.href = '../index.html'"
|
|
||||||
style="
|
|
||||||
padding: 8px 16px;
|
|
||||||
background: #007bff;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
← メインメニューに戻る
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<h1>給与管理システム</h1>
|
|
||||||
<p>従業員の給与計算、扶養管理、保険料・税金の管理を行います</p>
|
|
||||||
|
|
||||||
<div class="menu-grid">
|
|
||||||
<div
|
|
||||||
class="menu-card"
|
|
||||||
onclick="location.href = 'payroll-employees.html'"
|
|
||||||
>
|
|
||||||
<div class="icon">👥</div>
|
|
||||||
<h2>従業員管理</h2>
|
|
||||||
<p>従業員の基本情報、扶養家族情報を管理します</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="menu-card"
|
|
||||||
onclick="location.href = 'payroll-salary-settings.html'"
|
|
||||||
>
|
|
||||||
<div class="icon">💰</div>
|
|
||||||
<h2>給与設定</h2>
|
|
||||||
<p>各従業員の給与設定、基本給、手当などを管理します</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="menu-card"
|
|
||||||
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="icon">⚙️</div>
|
|
||||||
<h2>税率・保険料設定</h2>
|
|
||||||
<p>社会保険料率、所得税率表の管理を行います</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
style="
|
|
||||||
margin-top: 50px;
|
|
||||||
padding: 20px;
|
|
||||||
background: #f0f8ff;
|
|
||||||
border-radius: 5px;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<h3>📋 給与システムの使い方</h3>
|
|
||||||
<ol>
|
|
||||||
<li>
|
|
||||||
<strong>従業員管理</strong
|
|
||||||
>で従業員を登録し、扶養家族情報を入力します
|
|
||||||
</li>
|
|
||||||
<li><strong>給与設定</strong>で各従業員の基本給や手当を設定します</li>
|
|
||||||
<li>
|
|
||||||
<strong>税率・保険料設定</strong
|
|
||||||
>で社会保険料率や所得税率表を確認・更新します
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>月次給与計算</strong
|
|
||||||
>で勤怠情報を入力し、給与を計算・承認します
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
BIN
docs/r8ippan3.xlsx
Normal file
BIN
docs/r8ippan3.xlsx
Normal file
Binary file not shown.
BIN
docs/~$r8ippan3.xlsx
Normal file
BIN
docs/~$r8ippan3.xlsx
Normal file
Binary file not shown.
Reference in New Issue
Block a user