修正
This commit is contained in:
@@ -108,10 +108,22 @@
|
||||
font-size: 1.2em;
|
||||
color: #007bff;
|
||||
}
|
||||
.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">
|
||||
<a href="payroll.html" class="back-link">← 給与管理メニューに戻る</a>
|
||||
|
||||
<h1>給与管理 - 月次給与計算</h1>
|
||||
|
||||
<div class="filters">
|
||||
@@ -188,7 +200,13 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>支給日 *</label>
|
||||
<input type="date" name="payment_date" required />
|
||||
<input
|
||||
type="date"
|
||||
name="payment_date"
|
||||
required
|
||||
min="1900-01-01"
|
||||
max="2099-12-31"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -292,7 +310,7 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const API_BASE = "http://localhost:8000";
|
||||
const API_BASE = "http://localhost:18080";
|
||||
|
||||
async function loadEmployees() {
|
||||
const response = await fetch(
|
||||
@@ -436,19 +454,59 @@
|
||||
);
|
||||
const payroll = await response.json();
|
||||
|
||||
// 従業員情報と扶養家族情報を取得
|
||||
const empResponse = await fetch(
|
||||
`${API_BASE}/payroll/employees/${payroll.employee_id}`
|
||||
);
|
||||
const employee = await empResponse.json();
|
||||
|
||||
// 源泉税控除対象人数を計算(19歳以上のみ)
|
||||
const dependentCount = employee.dependents
|
||||
? employee.dependents.filter((d) => {
|
||||
// 有効期間チェック
|
||||
if (d.valid_to && new Date(d.valid_to) < new Date())
|
||||
return false;
|
||||
|
||||
// 生年月日から年齢を計算(その年の12月31日時点)
|
||||
if (!d.birth_date) return false;
|
||||
const birthDate = new Date(d.birth_date);
|
||||
const targetYear = payroll.payroll_year;
|
||||
const yearEnd = new Date(targetYear, 11, 31); // 12月31日
|
||||
let age = yearEnd.getFullYear() - birthDate.getFullYear();
|
||||
const monthDiff = yearEnd.getMonth() - birthDate.getMonth();
|
||||
if (
|
||||
monthDiff < 0 ||
|
||||
(monthDiff === 0 && yearEnd.getDate() < birthDate.getDate())
|
||||
) {
|
||||
age--;
|
||||
}
|
||||
|
||||
// 19歳以上のみ源泉税控除対象(16-18歳は住民税のみ)
|
||||
return age >= 19;
|
||||
}).length
|
||||
: 0;
|
||||
|
||||
const html = `
|
||||
<h2>${payroll.payroll_year}年${
|
||||
payroll.payroll_month
|
||||
}月 給与明細</h2>
|
||||
<p>従業員ID: ${payroll.employee_id} | 支給日: ${
|
||||
<p><strong>従業員:</strong> ${employee.employee_code} - ${
|
||||
employee.name
|
||||
} | <strong>支給日:</strong> ${
|
||||
payroll.payment_date
|
||||
}</p>
|
||||
} | <strong>甲欄扶養親族人数(源泉税控除対象):</strong> ${dependentCount}人</p>
|
||||
<p>ステータス: <span class="status-badge status-${
|
||||
payroll.status
|
||||
}">${getStatusLabel(payroll.status)}</span></p>
|
||||
|
||||
<div class="detail-section">
|
||||
<h3>勤怠情報</h3>
|
||||
<h3>勤怠情報 ${
|
||||
payroll.status === "calculated"
|
||||
? '<button class="btn btn-primary" onclick="editPayroll(' +
|
||||
payroll.payroll_id +
|
||||
')" style="padding: 5px 10px; font-size: 12px; float: right;">編集</button>'
|
||||
: ""
|
||||
}</h3>
|
||||
<div class="detail-row"><span>出勤日数:</span><span>${
|
||||
payroll.working_days
|
||||
}日</span></div>
|
||||
@@ -493,15 +551,27 @@
|
||||
<div class="detail-row"><span>健康保険:</span><span>¥${Number(
|
||||
payroll.health_insurance
|
||||
).toLocaleString()}</span></div>
|
||||
<div class="detail-row"><span>介護保険:</span><span>¥${Number(
|
||||
payroll.care_insurance || 0
|
||||
).toLocaleString()}</span></div>
|
||||
<div class="detail-row"><span>厚生年金:</span><span>¥${Number(
|
||||
payroll.pension_insurance
|
||||
).toLocaleString()}</span></div>
|
||||
<div class="detail-row"><span>雇用保険:</span><span>¥${Number(
|
||||
payroll.employment_insurance
|
||||
).toLocaleString()}</span></div>
|
||||
<div class="detail-row"><span>所得税:</span><span>¥${Number(
|
||||
payroll.income_tax
|
||||
).toLocaleString()}</span></div>
|
||||
<div class="detail-row">
|
||||
<span>所得税 <small style="color:#666;">(甲欄${dependentCount}人)</small>:</span>
|
||||
<span>¥${Number(
|
||||
payroll.income_tax
|
||||
).toLocaleString()}</span>
|
||||
</div>
|
||||
<div style="padding: 5px 10px; background: #f0f8ff; border-radius: 3px; margin: 5px 0; font-size: 12px; color: #666;">
|
||||
<small>
|
||||
※課税対象額から源泉徴収税額表(甲欄)を参照して計算<br>
|
||||
扶養親族${dependentCount}人(19歳以上のみ対象)
|
||||
</small>
|
||||
</div>
|
||||
<div class="detail-row"><span>住民税:</span><span>¥${Number(
|
||||
payroll.resident_tax
|
||||
).toLocaleString()}</span></div>
|
||||
@@ -544,6 +614,119 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function editPayroll(payrollId) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/calculation/${payrollId}`
|
||||
);
|
||||
const payroll = await response.json();
|
||||
|
||||
// 編集フォームを表示
|
||||
const html = `
|
||||
<div style="position: fixed; top: 50px; left: 50%; transform: translateX(-50%); width: 800px; max-height: 90vh; overflow-y: auto; background: white; padding: 30px; border: 2px solid #007bff; border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.2); z-index: 1000;">
|
||||
<h2>給与データ編集</h2>
|
||||
<form id="editPayrollForm" onsubmit="savePayrollEdit(event, ${payrollId})">
|
||||
<div class="detail-section">
|
||||
<h3>勤怠情報</h3>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label>出勤日数</label>
|
||||
<input type="number" name="working_days" step="0.5" value="${payroll.working_days}" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>勤務時間</label>
|
||||
<input type="number" name="working_hours" step="0.5" value="${payroll.working_hours}" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>残業時間</label>
|
||||
<input type="number" name="overtime_hours" step="0.5" value="${payroll.overtime_hours}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label>休日出勤時間</label>
|
||||
<input type="number" name="holiday_hours" step="0.5" value="${payroll.holiday_hours}" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>遅刻時間</label>
|
||||
<input type="number" name="late_hours" step="0.5" value="${payroll.late_hours}" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>欠勤日数</label>
|
||||
<input type="number" name="absent_days" step="0.5" value="${payroll.absent_days}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<h3>その他</h3>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label>その他手当</label>
|
||||
<input type="number" name="other_allowance" step="0.01" value="${payroll.other_allowance}" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>住民税</label>
|
||||
<input type="number" name="resident_tax" step="0.01" value="${payroll.resident_tax}" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>その他控除</label>
|
||||
<input type="number" name="other_deduction" step="0.01" value="${payroll.other_deduction}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">保存して再計算</button>
|
||||
<button type="button" class="btn btn-secondary" onclick="this.closest('div').remove()">キャンセル</button>
|
||||
</form>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const editDiv = document.createElement("div");
|
||||
editDiv.innerHTML = html;
|
||||
document.body.appendChild(editDiv);
|
||||
} catch (error) {
|
||||
alert("給与データの取得に失敗しました");
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function savePayrollEdit(event, payrollId) {
|
||||
event.preventDefault();
|
||||
const form = event.target;
|
||||
const formData = new FormData(form);
|
||||
const data = {};
|
||||
|
||||
formData.forEach((value, key) => {
|
||||
data[key] = isNaN(value) || value === "" ? value : Number(value);
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE}/payroll/calculation/${payrollId}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(data),
|
||||
}
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
alert("給与データを更新しました");
|
||||
form.closest("div").remove();
|
||||
|
||||
// 再計算
|
||||
await recalculatePayroll(payrollId);
|
||||
} else {
|
||||
const error = await response.json();
|
||||
alert("更新に失敗しました: " + error.detail);
|
||||
}
|
||||
} catch (error) {
|
||||
alert("更新に失敗しました");
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function approvePayroll(payrollId) {
|
||||
if (!confirm("この給与を承認しますか?")) return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user