給与管理システム新規作成

This commit is contained in:
admin
2026-01-20 11:15:49 +09:00
parent 9dc7cbcb07
commit 8a00de8f03
45 changed files with 8925 additions and 5 deletions

View File

@@ -0,0 +1,302 @@
<!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 = 'http://localhost:8000';
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>