Files
njts-accounting-core/frontend/payroll-salary-settings.html
2026-02-07 20:04:42 +09:00

482 lines
16 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>
<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;
}
.back-link {
display: inline-block;
margin-bottom: 20px;
color: #007bff;
text-decoration: none;
font-size: 14px;
}
.back-link:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="container">
<button
onclick="location.href = 'payroll.html'"
class="back-button"
style="
padding: 8px 16px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
margin-bottom: 20px;
"
>
← 給与管理メニューに戻る
</button>
<h1>給与管理 - 給与設定</h1>
<button class="btn btn-primary" onclick="showAddForm()">
新規設定追加
</button>
<div id="settingsList" style="margin-top: 20px"></div>
<div
id="addForm"
style="
display: none;
margin-top: 20px;
padding: 20px;
border: 2px solid #007bff;
border-radius: 5px;
"
>
<h3 id="formTitle">給与設定の追加</h3>
<form onsubmit="saveSalarySetting(event)">
<input type="hidden" name="setting_id" id="setting_id" />
<div class="form-group">
<label>従業員 *</label>
<select name="employee_id" id="employee_id" required>
<option value="">選択してください</option>
</select>
</div>
<div class="form-group">
<label>基本給 (円) *</label>
<input
type="number"
name="base_salary"
id="base_salary"
step="0.01"
required
/>
</div>
<div class="form-group">
<label>時給 (円)</label>
<input
type="number"
name="hourly_rate"
id="hourly_rate"
step="0.01"
/>
<small>時給制の場合に入力してください</small>
</div>
<div class="form-group">
<label>雇用形態 *</label>
<select name="employment_type" id="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" id="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"
id="commute_allowance"
step="0.01"
value="0"
/>
</div>
<div class="form-group">
<label>その他手当 (円)</label>
<input
type="number"
name="other_allowance"
id="other_allowance"
step="0.01"
value="0"
/>
</div>
<div class="form-group">
<label style="display: flex; align-items: center">
<input
type="checkbox"
name="employment_insurance_eligible"
id="employment_insurance_eligible"
checked
style="width: auto; margin-right: 8px"
/>
<span>雇用保険加入対象</span>
</label>
<small>チェックを入れると雇用保険に加入します</small>
</div>
<div class="form-group">
<label>適用開始日 *</label>
<input
type="date"
name="valid_from"
id="valid_from"
required
min="1900-01-01"
max="2099-12-31"
/>
</div>
<div class="form-group">
<label>適用終了日</label>
<input
type="date"
name="valid_to"
id="valid_to"
min="1900-01-01"
max="2099-12-31"
/>
<small>通常は空欄のままにしてください</small>
</div>
<button type="submit" class="btn btn-primary">保存</button>
<button
type="button"
class="btn btn-secondary"
onclick="hideAddForm()"
>
キャンセル
</button>
</form>
</div>
</div>
<script>
const API_BASE = "";
let employees = [];
async function loadEmployees() {
try {
const response = await fetch(
`${API_BASE}/payroll/employees/?is_active=true`,
);
employees = await response.json();
const select = document.getElementById("employee_id");
select.innerHTML =
'<option value="">選択してください</option>' +
employees
.map(
(emp) =>
`<option value="${emp.employee_id}">${emp.employee_code} - ${emp.name}</option>`,
)
.join("");
loadAllSalarySettings();
} catch (error) {
alert("従業員一覧の取得に失敗しました");
console.error(error);
}
}
async function loadAllSalarySettings() {
try {
// すべての従業員の給与設定を取得
const allSettings = [];
for (const emp of employees) {
try {
const response = await fetch(
`${API_BASE}/payroll/settings/salary/${emp.employee_id}/current`,
);
if (response.ok) {
const setting = await response.json();
allSettings.push({
...setting,
employee_code: emp.employee_code,
employee_name: emp.name,
employee_id: emp.employee_id,
});
}
} catch (error) {
console.error(
`従業員 ${emp.employee_id} の給与設定取得失敗`,
error,
);
}
}
displayAllSettings(allSettings);
} catch (error) {
alert("給与設定の取得に失敗しました");
console.error(error);
}
}
function displayAllSettings(settings) {
const html = `
<table>
<thead>
<tr>
<th>従業員コード</th>
<th>氏名</th>
<th>基本給</th>
<th>時給</th>
<th>雇用形態</th>
<th>支給形態</th>
<th>通勤手当</th>
<th>その他手当</th>
<th>雇用保険</th>
<th>適用期間</th>
<th>操作</th>
</tr>
</thead>
<tbody>
${
settings.length > 0
? settings
.map(
(s) => `
<tr>
<td>${s.employee_code}</td>
<td>${s.employee_name}</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>
<td>${
s.employment_insurance_eligible !== false
? "✓ 対象"
: "✗ 非対象"
}</td>
<td>${s.valid_from} ${s.valid_to || "現在"}</td>
<td>
<button class="btn btn-primary" onclick="editSetting(${
s.setting_id
})" style="padding: 5px 10px; font-size: 12px;">編集</button>
</td>
</tr>
`,
)
.join("")
: '<tr><td colspan="11" style="text-align:center;">給与設定が登録されていません</td></tr>'
}
</tbody>
</table>
`;
document.getElementById("settingsList").innerHTML = html;
}
async function editSetting(settingId) {
try {
// 設定IDから詳細を取得する必要がありますが、現在のAPIでは難しいので
// 全従業員の設定を取得して該当するものを探す
for (const emp of employees) {
const response = await fetch(
`${API_BASE}/payroll/settings/salary/${emp.employee_id}`,
);
const settings = await response.json();
const setting = settings.find((s) => s.setting_id === settingId);
if (setting) {
// フォームに値を設定
document.getElementById("formTitle").textContent =
"給与設定の編集";
document.getElementById("setting_id").value = setting.setting_id;
document.getElementById("employee_id").value = emp.employee_id;
document.getElementById("employee_id").disabled = true; // 従業員は変更不可
document.getElementById("base_salary").value =
setting.base_salary;
document.getElementById("hourly_rate").value =
setting.hourly_rate || "";
document.getElementById("employment_type").value =
setting.employment_type;
document.getElementById("payment_type").value =
setting.payment_type;
document.getElementById("commute_allowance").value =
setting.commute_allowance;
document.getElementById("other_allowance").value =
setting.other_allowance;
document.getElementById("employment_insurance_eligible").checked =
setting.employment_insurance_eligible !== false;
document.getElementById("valid_from").value = setting.valid_from;
document.getElementById("valid_to").value =
setting.valid_to || "";
document.getElementById("addForm").style.display = "block";
return;
}
}
} catch (error) {
alert("給与設定の取得に失敗しました");
console.error(error);
}
}
function showAddForm() {
document.getElementById("formTitle").textContent = "給与設定の追加";
document.getElementById("addForm").style.display = "block";
document.querySelector("#addForm form").reset();
document.getElementById("setting_id").value = "";
document.getElementById("employee_id").disabled = false;
// デフォルト値設定
document.getElementById("valid_from").value = new Date()
.toISOString()
.split("T")[0];
document.getElementById("commute_allowance").value = "0";
document.getElementById("other_allowance").value = "0";
}
function hideAddForm() {
document.getElementById("addForm").style.display = "none";
document.querySelector("#addForm form").reset();
document.getElementById("employee_id").disabled = false;
}
async function saveSalarySetting(event) {
event.preventDefault();
const form = event.target;
const formData = new FormData(form);
const settingId = document.getElementById("setting_id").value;
const data = {
employee_id: parseInt(formData.get("employee_id")),
base_salary: parseFloat(formData.get("base_salary")) || 0,
employment_type: formData.get("employment_type"),
payment_type: formData.get("payment_type"),
commute_allowance: parseFloat(formData.get("commute_allowance")) || 0,
other_allowance: parseFloat(formData.get("other_allowance")) || 0,
employment_insurance_eligible: document.getElementById(
"employment_insurance_eligible",
).checked,
valid_from: formData.get("valid_from"),
};
if (formData.get("hourly_rate")) {
data.hourly_rate = parseFloat(formData.get("hourly_rate"));
}
if (formData.get("valid_to")) {
data.valid_to = formData.get("valid_to");
}
try {
let response;
if (settingId) {
// 更新
response = await fetch(
`${API_BASE}/payroll/settings/salary/${settingId}`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
},
);
} else {
// 新規追加
response = await fetch(`${API_BASE}/payroll/settings/salary`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
}
if (response.ok) {
alert(
settingId ? "給与設定を更新しました" : "給与設定を保存しました",
);
hideAddForm();
loadAllSalarySettings();
} else {
const error = await response.json();
alert("保存に失敗しました: " + error.detail);
}
} catch (error) {
alert("保存に失敗しました");
console.error(error);
}
}
// 初期化
loadEmployees();
</script>
</body>
</html>