chore: 年度リストを昇順に変更

This commit is contained in:
admin
2026-06-07 21:58:20 +09:00
parent 91632ea5b6
commit 4a500ff5e9
25 changed files with 1531 additions and 67 deletions

View File

@@ -405,6 +405,29 @@
cursor: default;
}
/* ── 印刷ヘッダー ── */
.print-header {
display: none;
}
@media print {
.print-header {
display: block;
margin-bottom: 12px;
border-bottom: 2px solid #000;
padding-bottom: 8px;
}
.print-header .ph-company {
font-size: 16px;
font-weight: bold;
margin-bottom: 4px;
}
.print-header .ph-sub {
font-size: 12px;
display: flex;
gap: 24px;
}
}
/* ── 印刷スタイル ── */
@media print {
.no-print {
@@ -434,50 +457,133 @@
</style>
</head>
<body>
<!-- 印刷時のみ表示されるヘッダー -->
<div class="print-header">
<div class="ph-company" id="printCompany"></div>
<div class="ph-sub">
<span id="printBank"></span>
<span id="printDate"></span>
</div>
</div>
<div class="page-header no-print">
<h2>🏦 銀行明細照会</h2>
<div class="header-btns">
<button class="btn btn-orange" onclick="openImportModal()">
CSV取込
</button>
<button class="btn btn-blue" onclick="window.print()">印刷</button>
<button class="btn btn-blue" onclick="doPrint()">印刷</button>
<a class="btn btn-gray" href="index.html">← メニューへ戻る</a>
</div>
</div>
<!-- 印刷設定(画面のみ) -->
<div
class="search-form no-print"
style="padding: 10px 18px; background: #fff8e1; border-color: #ffe082"
>
<label style="font-weight: bold; white-space: nowrap">印刷設定:</label>
<label style="white-space: nowrap">会社名:</label>
<input
type="text"
id="printCompanyInput"
style="
width: 220px;
padding: 4px 8px;
border: 1px solid #999;
border-radius: 3px;
font-size: 13px;
"
placeholder="例:株式会社〇〇"
/>
</div>
<div id="msgBar" class="msg-bar no-print"></div>
<!-- 検索フォーム -->
<div class="search-form no-print">
<label>年:</label>
<input
type="number"
id="filterYear"
min="2000"
max="2099"
style="width: 80px"
/>
<label>月:</label>
<select id="filterMonth">
<option value="">全月</option>
<option value="1">1月</option>
<option value="2">2月</option>
<option value="3">3月</option>
<option value="4">4月</option>
<option value="5">5月</option>
<option value="6">6月</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
</select>
<label>銀行:</label>
<select id="filterBank">
<option value="">全銀行</option>
</select>
<button class="btn btn-primary" onclick="loadData()">検索</button>
<div
class="search-form no-print"
style="flex-direction: column; align-items: flex-start; gap: 10px"
>
<div
style="display: flex; align-items: center; flex-wrap: wrap; gap: 8px"
>
<label style="min-width: 3em">開始:</label>
<input
type="number"
id="startYear"
min="2000"
max="2099"
style="width: 72px"
placeholder="年"
/><span></span>
<select id="startMonth" style="width: 60px">
<option value=""></option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7月</option>
<option value="8">8月</option>
<option value="9">9月</option>
<option value="10">10月</option>
<option value="11">11月</option>
<option value="12">12月</option>
</select>
<input
type="number"
id="startDay"
min="1"
max="31"
style="width: 48px"
placeholder="日"
/><span></span>
<span style="margin: 0 6px; color: #999"></span>
<label style="min-width: 3em">終了:</label>
<input
type="number"
id="endYear"
min="2000"
max="2099"
style="width: 72px"
placeholder="年"
/><span></span>
<select id="endMonth" style="width: 60px">
<option value=""></option>
<option value="1">1月</option>
<option value="2">2月</option>
<option value="3">3月</option>
<option value="4">4月</option>
<option value="5">5月</option>
<option value="6">6月</option>
<option value="7">7月</option>
<option value="8">8月</option>
<option value="9">9月</option>
<option value="10">10月</option>
<option value="11">11月</option>
<option value="12">12月</option>
</select>
<input
type="number"
id="endDay"
min="1"
max="31"
style="width: 48px"
placeholder="日"
/><span></span>
</div>
<div
style="display: flex; align-items: center; flex-wrap: wrap; gap: 8px"
>
<label>銀行:</label>
<select id="filterBank">
<option value="">全銀行</option>
</select>
<button class="btn btn-primary" onclick="loadData()">検索</button>
<button class="btn btn-gray" onclick="clearDateFilter()">クリア</button>
</div>
</div>
<!-- 月別チップ -->
@@ -638,16 +744,114 @@
}
// ── 明細データ読み込み ──────────────────────────────────
function _lastDayOfMonth(year, month) {
return new Date(parseInt(year), parseInt(month), 0).getDate();
}
// 会社名をlocalStorageに保存・復元
const COMPANY_KEY = "bankPrintCompanyName";
document.addEventListener("DOMContentLoaded", function () {
const saved = localStorage.getItem(COMPANY_KEY) || "";
const input = document.getElementById("printCompanyInput");
if (input && saved) input.value = saved;
input &&
input.addEventListener("input", function () {
localStorage.setItem(COMPANY_KEY, this.value);
});
});
function doPrint() {
const company = (
document.getElementById("printCompanyInput").value || ""
).trim();
const bankVal = document.getElementById("filterBank").value;
// 期間ラベル生成
const sy = document.getElementById("startYear").value;
const sm = document.getElementById("startMonth").value;
const sd = document.getElementById("startDay").value;
const ey = document.getElementById("endYear").value;
const em = document.getElementById("endMonth").value;
const ed = document.getElementById("endDay").value;
let periodLabel = "";
if (sy || ey) {
const from = sy
? sy +
(sm ? "/" + String(sm).padStart(2, "0") : "") +
(sd ? "/" + String(sd).padStart(2, "0") : "")
: "";
const to = ey
? ey +
(em ? "/" + String(em).padStart(2, "0") : "") +
(ed ? "/" + String(ed).padStart(2, "0") : "")
: "";
if (from && to) periodLabel = " (" + from + " 〜 " + to + ")";
else if (from) periodLabel = " (" + from + " 〜)";
else if (to) periodLabel = " (〜 " + to + ")";
}
// 印刷ヘッダーを設定
document.getElementById("printCompany").textContent = company;
document.getElementById("printBank").textContent = bankVal
? "銀行:" + bankVal + periodLabel
: "期間:全期間" + periodLabel || "全期間";
const today = new Date();
const dateStr =
today.getFullYear() +
"年" +
(today.getMonth() + 1) +
"月" +
today.getDate() +
"日";
document.getElementById("printDate").textContent = "印刷日:" + dateStr;
window.print();
}
function clearDateFilter() {
[
"startYear",
"startMonth",
"startDay",
"endYear",
"endMonth",
"endDay",
].forEach((id) => {
const el = document.getElementById(id);
if (el) el.value = "";
});
loadData();
}
async function loadData() {
const year = document.getElementById("filterYear").value;
const month = document.getElementById("filterMonth").value;
const sy = document.getElementById("startYear").value.trim();
const sm = document.getElementById("startMonth").value;
const sd = document.getElementById("startDay").value.trim();
const ey = document.getElementById("endYear").value.trim();
const em = document.getElementById("endMonth").value;
const ed = document.getElementById("endDay").value.trim();
const bank = document.getElementById("filterBank").value;
hideMsg("msgBar");
const qp = [];
if (year) qp.push(`year=${year}`);
if (month) qp.push(`month=${month}`);
if (sy) {
const m = sm ? String(sm).padStart(2, "0") : "01";
const d = sd ? String(sd).padStart(2, "0") : "01";
qp.push(`date_from=${sy}-${m}-${d}`);
}
if (ey) {
const m = em ? String(em).padStart(2, "0") : "12";
let d;
if (ed) {
d = String(ed).padStart(2, "0");
} else if (em) {
d = String(_lastDayOfMonth(ey, em)).padStart(2, "0");
} else {
d = "31";
}
qp.push(`date_to=${ey}-${m}-${d}`);
}
if (bank) qp.push(`bank_name=${encodeURIComponent(bank)}`);
const url = "/bank-statements" + (qp.length ? "?" + qp.join("&") : "");
@@ -658,7 +862,7 @@
_allRows = data.rows;
_currentPage = 1;
renderMonthlyChips(data.monthly, year, month);
renderMonthlyChips(data.monthly, { sy, sm, sd, ey, em, ed });
renderStats();
renderTable();
updateBankFilter(data.banks);
@@ -682,18 +886,23 @@
}
// ── 月別チップ ──────────────────────────────────────────
function renderMonthlyChips(monthly, selYear, selMonth) {
function renderMonthlyChips(monthly, filter) {
const { sy, sm, sd, ey, em, ed } = filter || {};
const wrap = document.getElementById("monthlyChips");
wrap.innerHTML = "";
if (!monthly.length) return;
// 「全月」チップ
// 「全月」チップ:開始・終了が同じ年で月未指定の場合にアクティブ
const noMonthSelected = !sm && !em && !sd && !ed;
const allChip = document.createElement("span");
allChip.className =
"month-chip no-print" + (!selMonth ? " active" : "");
"month-chip no-print" + (noMonthSelected ? " active" : "");
allChip.textContent = "全月";
allChip.onclick = () => {
document.getElementById("filterMonth").value = "";
document.getElementById("startMonth").value = "";
document.getElementById("startDay").value = "";
document.getElementById("endMonth").value = "";
document.getElementById("endDay").value = "";
loadData();
};
wrap.appendChild(allChip);
@@ -702,15 +911,22 @@
const [y, mo] = m.ym.split("-");
const chip = document.createElement("span");
const isActive =
String(selYear) === y && String(selMonth) === String(parseInt(mo));
sy === y &&
ey === y &&
String(parseInt(sm || "0")) === String(parseInt(mo)) &&
String(parseInt(em || "0")) === String(parseInt(mo)) &&
!sd &&
!ed;
chip.className = "month-chip no-print" + (isActive ? " active" : "");
chip.innerHTML = `${parseInt(mo)}月 <small style="color:inherit;opacity:.8">${
m.count
}件</small>`;
chip.innerHTML = `${parseInt(mo)}月 <small style="color:inherit;opacity:.8">${m.count}件</small>`;
chip.title = `出金 ${fmt(m.total_debit)} / 入金 ${fmt(m.total_credit)}`;
chip.onclick = () => {
document.getElementById("filterYear").value = y;
document.getElementById("filterMonth").value = parseInt(mo);
document.getElementById("startYear").value = y;
document.getElementById("startMonth").value = parseInt(mo);
document.getElementById("startDay").value = "";
document.getElementById("endYear").value = y;
document.getElementById("endMonth").value = parseInt(mo);
document.getElementById("endDay").value = "";
loadData();
};
wrap.appendChild(chip);

View File

@@ -376,5 +376,4 @@ html {
padding: 20px 16px !important;
box-sizing: border-box !important;
}
}

View File

@@ -279,6 +279,72 @@
background: #f3eeff;
color: #6f42c1;
}
/* テーブル */
.tbl-wrap {
overflow-x: auto;
margin-top: 12px;
}
table.apply-tbl {
width: 100%;
border-collapse: collapse;
font-size: 12px;
}
table.apply-tbl th,
table.apply-tbl td {
border: 1px solid #dee2e6;
padding: 5px 8px;
white-space: nowrap;
vertical-align: top;
}
table.apply-tbl th {
background: #f1f3f5;
font-weight: 700;
}
table.apply-tbl tr:hover td {
background: #f8f9fa;
}
.proc-select {
width: 100%;
padding: 8px 10px;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 13px;
margin-bottom: 10px;
}
.arrive-badge {
font-weight: 700;
color: #0a3622;
background: #d1e7dd;
padding: 2px 8px;
border-radius: 4px;
font-family: monospace;
font-size: 12px;
cursor: pointer;
}
.info-box {
background: #fff3cd;
border: 1px solid #ffc107;
border-radius: 6px;
padding: 10px 14px;
font-size: 12px;
line-height: 1.7;
margin-bottom: 14px;
}
.env-badge {
display: inline-block;
padding: 2px 10px;
border-radius: 10px;
font-size: 11px;
font-weight: 700;
}
.env-sandbox {
background: #fff3cd;
color: #856404;
}
.env-production {
background: #d1e7dd;
color: #0a3622;
}
</style>
</head>
<body>
@@ -422,6 +488,193 @@
</div>
</div>
<!-- ─── 申請書作成(最終確認試験用) ───────────────────── -->
<div class="card" id="applyCard">
<h2>📝 テスト申請書作成(最終確認試験用)</h2>
<p class="desc">
検証環境へテスト手続を送信して到達番号を取得します。<br />
取得した到達番号は試験データ表と Excel
に記入してデジタル庁へ送付してください。
</p>
<div class="info-box">
⚠️ ログイン後に操作可能です。<br />
環境:
<span id="envLabel" class="env-badge env-sandbox">sandbox検証</span>
&nbsp; ソフトウェアID:
<code id="swIdLabel" style="font-size: 11px"></code>
</div>
<div class="form-group">
<label>手続識別子(対象手続を選択)</label>
<select class="proc-select" id="procSelect">
<optgroup label="電子送達関係">
<option value="900A013800001000">
900A013800001000 APIテスト用手続電子送達0001電子送達1件
</option>
</optgroup>
<optgroup label="社会保険関係 標準形式">
<option value="950A010002010000">
950A010002010000 APIテスト社保通1007 公文書(署名あり)
</option>
<option value="950A010002011000">
950A010002011000 APIテスト社保通1008 公文書(署名なし)
</option>
<option value="950A010700001000">
950A010700001000 APIテスト社保通1001 手続終了
</option>
<option value="950A010700002000">
950A010700002000 APIテスト社保通1002 手続終了
</option>
<option value="950A010700005000">
950A010700005000 APIテスト社保通1003 取下げ承認
</option>
<option value="950A010700006000">
950A010700006000 APIテスト社保通1004 取下げ却下
</option>
</optgroup>
<optgroup label="労働保険関係 標準形式">
<option value="950A010200003000">
950A010200003000 APIテスト労保通1001 手続終了
</option>
<option value="950A010200004000">
950A010200004000 APIテスト労保通1002 手続終了
</option>
<option value="950A010002012000">
950A010002012000 APIテスト労保通1004 コメント(メッセージ)
</option>
<option value="950A010002016000">
950A010002016000 APIテスト労保通1008 コメント(公文書)
</option>
</optgroup>
<optgroup label="社会保険関係 個別署名形式">
<option value="950A101220029000">
950A101220029000 APIテスト社保個1001 手続終了
</option>
<option value="950A102200038000">
950A102200038000 APIテスト社保個1005 コメント(メッセージ)
</option>
<option value="950A102200039000">
950A102200039000 APIテスト社保個1006 公文書(署名あり)
</option>
</optgroup>
<optgroup label="その他">
<option value="900A020700013000">
900A020700013000 APIテスト労保適用0003 補正申請
</option>
<option value="900A102800072000">
900A102800072000 APIテスト労保適用個0014 プレ印字
</option>
</optgroup>
</select>
</div>
<div
style="
display: flex;
gap: 10px;
align-items: center;
flex-wrap: wrap;
margin-bottom: 14px;
"
>
<button
class="btn btn-primary"
id="applyBtn"
onclick="doTestApply()"
style="background: #6f42c1"
>
📤 テスト申請を送信
</button>
<span
id="applySpinner"
style="display: none; font-size: 13px; color: #555"
>⏳ 送信中...</span
>
</div>
<!-- 結果テーブル -->
<div id="applyResult" style="display: none">
<div class="dl-result" style="margin-bottom: 10px">
<span>✅ 到達番号: </span>
<strong
id="arriveIdResult"
class="arrive-badge"
title="クリックでコピー"
onclick="copyArriveId()"
></strong>
<span
style="font-size: 12px; color: #6c757d; margin-left: 8px"
id="arriveDateResult"
></span>
</div>
<div style="font-size: 12px; color: #555" id="procNameResult"></div>
</div>
<div
id="applyError"
style="
display: none;
color: #842029;
font-size: 13px;
margin-top: 8px;
padding: 8px 12px;
background: #f8d7da;
border-radius: 4px;
"
></div>
</div>
<!-- ─── 申請案件一覧 ─────────────────────────────────────── -->
<div class="card" id="listCard">
<h2>📋 申請案件一覧(到達番号確認)</h2>
<p class="desc">
送信済みの申請案件一覧を表示します。到達番号を Excel
に転記してデジタル庁へ提出してください。
</p>
<div
style="display: flex; gap: 10px; margin-bottom: 12px; flex-wrap: wrap"
>
<button class="btn btn-secondary" onclick="loadApplyList()">
🔄 一覧を更新
</button>
<button
class="btn"
onclick="exportApplyListCsv()"
style="background: #0d6efd; color: #fff"
>
📥 CSV エクスポート
</button>
</div>
<div
id="listStatus"
style="font-size: 13px; color: #6c757d; margin-bottom: 8px"
></div>
<div class="tbl-wrap" id="applyListWrap">
<table class="apply-tbl">
<thead>
<tr>
<th>#</th>
<th>到達番号</th>
<th>到達日時</th>
<th>手続識別子</th>
<th>手続名</th>
<th>ステータス</th>
<th>サブステータス</th>
</tr>
</thead>
<tbody id="applyListBody">
<tr>
<td
colspan="7"
style="text-align: center; color: #aaa; padding: 20px"
>
「一覧を更新」ボタンで取得してください
</td>
</tr>
</tbody>
</table>
</div>
</div>
<script>
let timer = null;
let currentTab = "api";
@@ -677,10 +930,174 @@
const r = await fetch("/egov/status");
const d = await r.json();
apply(d);
// 環境・ソフトウェアID を表示
document.getElementById("envLabel").textContent =
d.environment === "sandbox"
? "sandbox検証環境"
: "production本番環境";
document.getElementById("envLabel").className =
"env-badge " +
(d.environment === "sandbox" ? "env-sandbox" : "env-production");
if (!["idle", "logged_in", "error"].includes(d.status))
startPolling();
} catch (_) {}
// ソフトウェアID 表示
try {
const r2 = await fetch("/egov/config-info");
if (r2.ok) {
const cfg = await r2.json();
document.getElementById("swIdLabel").textContent =
cfg.software_id || "—";
}
} catch (_) {}
})();
// ─── テスト申請書作成 ────────────────────────────────────
let _applyResults = [];
async function doTestApply() {
const procId = document.getElementById("procSelect").value;
const applyBtn = document.getElementById("applyBtn");
const spinner = document.getElementById("applySpinner");
const resultDiv = document.getElementById("applyResult");
const errorDiv = document.getElementById("applyError");
applyBtn.disabled = true;
show("applySpinner");
resultDiv.style.display = "none";
errorDiv.style.display = "none";
try {
const res = await fetch("/egov/test-apply", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ proc_id: procId }),
});
const data = await res.json();
if (!res.ok) {
errorDiv.textContent =
"エラー: " + (data.detail || JSON.stringify(data));
errorDiv.style.display = "block";
return;
}
document.getElementById("arriveIdResult").textContent =
data.arrive_id || "(取得中)";
document.getElementById("arriveDateResult").textContent =
data.arrive_date ? ` 到達: ${data.arrive_date}` : "";
document.getElementById("procNameResult").textContent =
data.proc_name || "";
resultDiv.style.display = "block";
// 送信済みリストに追加
_applyResults.unshift({
arrive_id: data.arrive_id,
arrive_date: data.arrive_date,
proc_id: procId,
proc_name: data.proc_name,
status: "",
sub_status: "",
});
renderApplyList(_applyResults);
} catch (err) {
errorDiv.textContent = "通信エラー: " + err.message;
errorDiv.style.display = "block";
} finally {
applyBtn.disabled = false;
hide("applySpinner");
}
}
function copyArriveId() {
const val = document.getElementById("arriveIdResult").textContent;
navigator.clipboard
.writeText(val)
.then(() => alert("コピーしました: " + val))
.catch(() => {});
}
// ─── 申請案件一覧 ────────────────────────────────────────
async function loadApplyList() {
const statusDiv = document.getElementById("listStatus");
statusDiv.textContent = "⏳ 一覧を取得中...";
try {
const res = await fetch("/egov/apply-list");
if (!res.ok) {
const e = await res.json();
statusDiv.textContent = "エラー: " + (e.detail || "不明");
return;
}
const data = await res.json();
_applyResults = data.apply_list || [];
renderApplyList(_applyResults);
statusDiv.textContent = `${data.total}`;
} catch (err) {
statusDiv.textContent = "通信エラー: " + err.message;
}
}
function renderApplyList(items) {
const tbody = document.getElementById("applyListBody");
if (!items.length) {
tbody.innerHTML =
'<tr><td colspan="7" style="text-align:center;color:#aaa;padding:20px;">申請案件はありません</td></tr>';
return;
}
tbody.innerHTML = items
.map(
(it, i) => `
<tr>
<td>${i + 1}</td>
<td><span class="arrive-badge" onclick="navigator.clipboard.writeText('${esc(it.arrive_id)}')" title="クリックでコピー">${esc(it.arrive_id)}</span></td>
<td>${esc(it.arrive_date)}</td>
<td style="font-family:monospace;font-size:11px">${esc(it.proc_id)}</td>
<td>${esc(it.proc_name)}</td>
<td>${esc(it.status)}</td>
<td>${esc(it.sub_status)}</td>
</tr>
`,
)
.join("");
}
function esc(s) {
return String(s || "")
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
}
function exportApplyListCsv() {
if (!_applyResults.length) {
alert("データがありません");
return;
}
const header = [
"到達番号",
"到達日時",
"手続識別子",
"手続名",
"ステータス",
"サブステータス",
];
const rows = _applyResults.map((it) =>
[
it.arrive_id,
it.arrive_date,
it.proc_id,
it.proc_name,
it.status,
it.sub_status,
]
.map((v) => `"${String(v || "").replace(/"/g, '""')}"`)
.join(","),
);
const csv = "\uFEFF" + [header.join(","), ...rows].join("\r\n");
const link = document.createElement("a");
link.href = URL.createObjectURL(new Blob([csv], { type: "text/csv" }));
link.download = "egov_apply_list.csv";
link.click();
}
</script>
</body>
</html>

View File

@@ -254,6 +254,41 @@ function initTrialBalance() {
}
}
// 年度ドロップダウンを読み込む
async function loadFiscalYears() {
try {
const res = await fetch("/trial-balance/fiscal-years");
if (!res.ok) return;
const years = await res.json();
const sel = document.getElementById("fiscalYear");
if (!sel) return;
years.forEach((fy) => {
const opt = document.createElement("option");
opt.value = JSON.stringify({
date_from: fy.date_from,
date_to: fy.date_to,
});
opt.textContent = fy.label;
sel.appendChild(opt);
});
} catch (e) {
console.warn("年度一覧取得失敗:", e);
}
}
loadFiscalYears();
// 年度選択 → 開始・終了日を自動セット(ユーザーは自由に変更可能)
document.getElementById("fiscalYear").addEventListener("change", function () {
if (!this.value) return;
try {
const { date_from, date_to } = JSON.parse(this.value);
document.getElementById("dateFrom").value = date_from;
document.getElementById("dateTo").value = date_to;
} catch (e) {
/* ignore */
}
});
// 檢索按鈕的事件
document.getElementById("btnSearch").addEventListener("click", () => {
const dateFrom = document.getElementById("dateFrom").value;

View File

@@ -1331,7 +1331,7 @@
</div>
<div class="calc-note" style="background:#eef6ee; border-left:3px solid #28a745; padding:6px 10px; margin:6px 0; font-size:0.88em; color:#1a5c1a; line-height:1.7">
健康保険 ¥${_health.toLocaleString()} 介護保険 ¥${_care.toLocaleString()} 子ども・子育て支援金 ¥${_child.toLocaleString()} <strong>¥${_healthCareChild.toLocaleString()}</strong><br>
¥${_healthCareChild.toLocaleString()}(上記合計) 厚生年金 ¥${_pension.toLocaleString()}${' '}${_emp > 0 ? ` 雇用保険 ¥${_emp.toLocaleString()}` : ''} <strong>社会保険小計 ¥${_socialSub.toLocaleString()}</strong>
¥${_healthCareChild.toLocaleString()}(上記合計) 厚生年金 ¥${_pension.toLocaleString()}${" "}${_emp > 0 ? ` 雇用保険 ¥${_emp.toLocaleString()}` : ""} <strong>社会保険小計 ¥${_socialSub.toLocaleString()}</strong>
</div>
<div class="detail-row"><span style="font-weight:bold">社会保険小計:</span><span style="font-weight:bold">¥${_socialSub.toLocaleString()}</span></div>
<div class="detail-row">
@@ -1379,7 +1379,12 @@
document.getElementById("payrollDetail").style.display = "block";
// モバイルでは明細パネルへ自動スクロール
if (window.innerWidth <= 767 && splitRightSalary) {
setTimeout(function() { splitRightSalary.scrollIntoView({ behavior: "smooth", block: "start" }); }, 50);
setTimeout(function () {
splitRightSalary.scrollIntoView({
behavior: "smooth",
block: "start",
});
}, 50);
}
} catch (error) {
alert("給与明細の取得に失敗しました");
@@ -1918,7 +1923,7 @@
</div>
<div class="calc-note" style="background:#eef6ee; border-left:3px solid #28a745; padding:6px 10px; margin:6px 0; font-size:0.88em; color:#1a5c1a; line-height:1.7">
健康保険 ¥${_bHealth.toLocaleString()} 介護保険 ¥${_bCare.toLocaleString()} 子ども・子育て支援金 ¥${_bChild.toLocaleString()} <strong>¥${_bHealthCareChild.toLocaleString()}</strong><br>
¥${_bHealthCareChild.toLocaleString()}(上記合計) 厚生年金 ¥${_bPension.toLocaleString()}${' '}${_bEmp > 0 ? ` 雇用保険 ¥${_bEmp.toLocaleString()}` : ''} <strong>社会保険小計 ¥${_bSocialSub.toLocaleString()}</strong>
¥${_bHealthCareChild.toLocaleString()}(上記合計) 厚生年金 ¥${_bPension.toLocaleString()}${" "}${_bEmp > 0 ? ` 雇用保険 ¥${_bEmp.toLocaleString()}` : ""} <strong>社会保険小計 ¥${_bSocialSub.toLocaleString()}</strong>
</div>
<div class="detail-row"><span style="font-weight:bold">社会保険小計:</span><span style="font-weight:bold">¥${_bSocialSub.toLocaleString()}</span></div>
<div class="detail-row">
@@ -1964,7 +1969,12 @@
document.getElementById("bonusDetail").style.display = "block";
// モバイルでは明細パネルへ自動スクロール
if (window.innerWidth <= 767 && splitRightBonus) {
setTimeout(function() { splitRightBonus.scrollIntoView({ behavior: "smooth", block: "start" }); }, 50);
setTimeout(function () {
splitRightBonus.scrollIntoView({
behavior: "smooth",
block: "start",
});
}, 50);
}
} catch (error) {
alert("賞与明細の取得に失敗しました");
@@ -2638,19 +2648,33 @@
// CSVデータを生成
let csvContent =
"データ,従業員コード,従業員名,年月,支給額,控除額,差引支給額\n";
"データ,従業員コード,従業員名,年月,支給額,社会保険,所得税,控除額合計,差引支給額\n";
// 給与データ
if (result.data.salary) {
(result.data.salary || []).forEach((salary) => {
csvContent += `給与,${salary.employee_code},${salary.employee_name},${salary.payroll_year}-${String(salary.payroll_month).padStart(2, "0")},${salary.total_payment},${salary.total_deduction},${salary.net_payment}\n`;
const socialIns =
Number(salary.health_insurance || 0) +
Number(salary.care_insurance || 0) +
Number(salary.pension_insurance || 0) +
Number(salary.employment_insurance || 0) +
Number(salary.child_support || 0);
const incomeTax = Number(salary.income_tax || 0);
csvContent += `給与,${salary.employee_code},${salary.employee_name},${salary.payroll_year}-${String(salary.payroll_month).padStart(2, "0")},${salary.total_payment},${socialIns},${incomeTax},${salary.total_deduction},${salary.net_payment}\n`;
});
}
// 賞与データ
if (result.data.bonus) {
(result.data.bonus || []).forEach((bonus) => {
csvContent += `賞与,${bonus.employee_code},${bonus.employee_name},${bonus.bonus_year}-${String(bonus.bonus_month).padStart(2, "0")},${bonus.total_bonus},${bonus.total_deduction},${bonus.net_bonus}\n`;
const socialIns =
Number(bonus.health_insurance || 0) +
Number(bonus.care_insurance || 0) +
Number(bonus.pension_insurance || 0) +
Number(bonus.employment_insurance || 0) +
Number(bonus.child_support || 0);
const incomeTax = Number(bonus.income_tax || 0);
csvContent += `賞与,${bonus.employee_code},${bonus.employee_name},${bonus.bonus_year}-${String(bonus.bonus_month).padStart(2, "0")},${bonus.total_bonus},${socialIns},${incomeTax},${bonus.total_deduction},${bonus.net_bonus}\n`;
});
}

View File

@@ -292,6 +292,20 @@
</div>
<div class="search-form">
<label for="fiscalYear">年度:</label>
<select
id="fiscalYear"
style="
padding: 4px 8px;
border: 1px solid #ccc;
border-radius: 3px;
font-size: 13px;
min-width: 200px;
"
>
<option value="">-- 選択してください --</option>
</select>
<label for="dateFrom">開始年月日:</label>
<input type="date" id="dateFrom" min="1900-01-01" max="2099-12-31" />