修正
This commit is contained in:
@@ -52,7 +52,13 @@
|
||||
|
||||
<div class="row">
|
||||
<label>仕訳日</label>
|
||||
<input type="date" id="entryDate" onchange="onEntryDateChanged()" />
|
||||
<input
|
||||
type="date"
|
||||
id="entryDate"
|
||||
min="1900-01-01"
|
||||
max="2999-12-31"
|
||||
onchange="onEntryDateChanged()"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@@ -60,9 +66,11 @@
|
||||
<input
|
||||
type="text"
|
||||
id="description"
|
||||
style="flex: 1"
|
||||
list="descriptionHistory"
|
||||
style="flex: 3; min-width: 600px"
|
||||
placeholder="例:売上入金/経費支払 など"
|
||||
/>
|
||||
<datalist id="descriptionHistory"></datalist>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@@ -100,6 +108,8 @@
|
||||
<button onclick="submitJournal()">登録</button>
|
||||
</div>
|
||||
|
||||
<hr style="margin: 40px 0; border: none; border-top: 2px solid #ddd" />
|
||||
|
||||
<h3>月次锁定管理</h3>
|
||||
|
||||
<div style="margin-bottom: 8px">
|
||||
@@ -148,6 +158,43 @@
|
||||
🔒 该月份已锁定,不能编辑仕訳
|
||||
</div>
|
||||
|
||||
<!-- ========================================
|
||||
仕訳検索セクション(journal-list.htmlから統合)
|
||||
======================================== -->
|
||||
<hr style="margin: 40px 0; border: none; border-top: 2px solid #ddd" />
|
||||
|
||||
<h2>仕訳検索</h2>
|
||||
|
||||
<div style="margin: 16px 0">
|
||||
<label>期間</label>
|
||||
<input
|
||||
type="date"
|
||||
id="searchFromDate"
|
||||
min="1900-01-01"
|
||||
max="2999-12-31"
|
||||
/>
|
||||
~
|
||||
<input type="date" id="searchToDate" min="1900-01-01" max="2999-12-31" />
|
||||
|
||||
<label style="margin-left: 16px">摘要</label>
|
||||
<input type="text" id="searchKeyword" />
|
||||
|
||||
<button onclick="searchJournals()" style="margin-left: 8px">検索</button>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>日付</th>
|
||||
<th>摘要</th>
|
||||
<th>借方合計</th>
|
||||
<th>貸方合計</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="searchResultList"></tbody>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
let rowSeq = 0; // 各行唯一ID
|
||||
|
||||
@@ -229,10 +276,50 @@
|
||||
.slice(0, 10);
|
||||
|
||||
await onEntryDateChanged();
|
||||
|
||||
// 摘要履歴を読み込み
|
||||
loadDescriptionHistory();
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
// -----------------------------
|
||||
// 摘要履歴管理
|
||||
// -----------------------------
|
||||
function loadDescriptionHistory() {
|
||||
const history = JSON.parse(
|
||||
localStorage.getItem("descriptionHistory") || "[]"
|
||||
);
|
||||
const datalist = document.getElementById("descriptionHistory");
|
||||
datalist.innerHTML = "";
|
||||
|
||||
history.forEach((desc) => {
|
||||
const option = document.createElement("option");
|
||||
option.value = desc;
|
||||
datalist.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
function saveDescriptionToHistory(description) {
|
||||
if (!description || description.trim() === "") return;
|
||||
|
||||
let history = JSON.parse(
|
||||
localStorage.getItem("descriptionHistory") || "[]"
|
||||
);
|
||||
|
||||
// 重複を避け、先頭に追加
|
||||
history = history.filter((d) => d !== description);
|
||||
history.unshift(description);
|
||||
|
||||
// 最大20件まで保存
|
||||
if (history.length > 20) {
|
||||
history = history.slice(0, 20);
|
||||
}
|
||||
|
||||
localStorage.setItem("descriptionHistory", JSON.stringify(history));
|
||||
loadDescriptionHistory();
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// 仮払/仮受 セレクタ
|
||||
// -----------------------------
|
||||
@@ -490,6 +577,10 @@
|
||||
}
|
||||
|
||||
const result = await res.json();
|
||||
|
||||
// 摘要を履歴に保存
|
||||
saveDescriptionToHistory(description);
|
||||
|
||||
alert(`登録完了(ID=${result.journal_entry_id})`);
|
||||
location.reload();
|
||||
}
|
||||
@@ -664,6 +755,256 @@
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// 仕訳検索機能(journal-list.htmlから統合)
|
||||
// ========================================
|
||||
async function searchJournals() {
|
||||
const from = document.getElementById("searchFromDate").value;
|
||||
const to = document.getElementById("searchToDate").value;
|
||||
const key = document.getElementById("searchKeyword").value;
|
||||
|
||||
// 検索条件を保存
|
||||
localStorage.setItem(
|
||||
"journalSearchConditions",
|
||||
JSON.stringify({
|
||||
fromDate: from,
|
||||
toDate: to,
|
||||
keyword: key,
|
||||
})
|
||||
);
|
||||
|
||||
const qs = new URLSearchParams();
|
||||
if (from) qs.append("from_date", from);
|
||||
if (to) qs.append("to_date", to);
|
||||
if (key) qs.append("keyword", key);
|
||||
|
||||
const res = await fetch(`${API}/journal-entries?${qs.toString()}`);
|
||||
const data = await res.json();
|
||||
|
||||
const tbody = document.getElementById("searchResultList");
|
||||
tbody.innerHTML = "";
|
||||
|
||||
data.forEach((e) => {
|
||||
const tr = document.createElement("tr");
|
||||
tr.innerHTML = `
|
||||
<td>${e.entry_date}</td>
|
||||
<td>${e.description}</td>
|
||||
<td style="text-align:right">${e.debit_total.toLocaleString()}</td>
|
||||
<td style="text-align:right">${e.credit_total.toLocaleString()}</td>
|
||||
<td>
|
||||
<button onclick="viewJournal(${e.journal_entry_id})">表示</button>
|
||||
<button onclick="copyJournal(${
|
||||
e.journal_entry_id
|
||||
})" style="color: green;">複製</button>
|
||||
<button onclick="reverseAndEdit(${e.journal_entry_id})">修正</button>
|
||||
<button onclick="deleteJournalEntry(${
|
||||
e.journal_entry_id
|
||||
})" style="color: red;">削除</button>
|
||||
</td>
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
function viewJournal(id) {
|
||||
window.open(`journal-view.html?id=${id}`, "_blank");
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// 仕訳複製:既存の仕訳を入力フォームにコピー
|
||||
// --------------------------------------------------
|
||||
async function copyJournal(id) {
|
||||
try {
|
||||
const res = await fetch(`${API}/journal-entries/${id}`);
|
||||
if (!res.ok) {
|
||||
alert("仕訳の取得に失敗しました");
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
// 日付は今日の日付をデフォルトに(必要に応じて変更可能)
|
||||
document.getElementById("entryDate").value = new Date()
|
||||
.toISOString()
|
||||
.slice(0, 10);
|
||||
|
||||
// 摘要をコピー
|
||||
document.getElementById("description").value = data.description || "";
|
||||
|
||||
// 既存の行をすべてクリア(税行も含む)
|
||||
const tbody = document.querySelector("#linesTable tbody");
|
||||
tbody.innerHTML = "";
|
||||
rowSeq = 0;
|
||||
|
||||
// 仕訳明細をコピー
|
||||
if (data.lines && data.lines.length > 0) {
|
||||
data.lines.forEach((line) => {
|
||||
const tr = addRow();
|
||||
|
||||
// 科目を設定
|
||||
const accountSelect = tr.querySelector(".account");
|
||||
if (accountSelect) {
|
||||
accountSelect.value = line.account_id;
|
||||
}
|
||||
|
||||
// 借方・貸方金額を設定
|
||||
const debitInput = tr.querySelector(".debit");
|
||||
const creditInput = tr.querySelector(".credit");
|
||||
if (debitInput && line.debit) {
|
||||
debitInput.value = line.debit;
|
||||
}
|
||||
if (creditInput && line.credit) {
|
||||
creditInput.value = line.credit;
|
||||
}
|
||||
|
||||
// 税区分を設定
|
||||
const taxTypeSelect = tr.querySelector(".taxType");
|
||||
const taxDirSelect = tr.querySelector(".taxDirection");
|
||||
|
||||
if (line.tax_rate && taxTypeSelect) {
|
||||
taxTypeSelect.value = String(line.tax_rate);
|
||||
}
|
||||
|
||||
if (line.tax_direction && taxDirSelect) {
|
||||
taxDirSelect.value = line.tax_direction;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ページ上部の仕訳入力フォームにスクロール
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
|
||||
alert(
|
||||
"仕訳を複製しました。日付や金額などを修正して登録してください。"
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
alert(`エラーが発生しました: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// 修正仕訳:逆仕訳を作って修正画面へ遷移
|
||||
// --------------------------------------------------
|
||||
async function reverseAndEdit(id) {
|
||||
if (
|
||||
!confirm(
|
||||
"この仕訳の逆仕訳を作成し、修正仕訳入力画面を開きます。よろしいですか?"
|
||||
)
|
||||
)
|
||||
return;
|
||||
|
||||
try {
|
||||
// ① 逆仕訳を作成
|
||||
const res = await fetch(`${API}/journal-entries/${id}/reverse`, {
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}));
|
||||
alert(
|
||||
data.detail || `逆仕訳の作成に失敗しました (HTTP ${res.status})`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
alert(
|
||||
`逆仕訳を作成しました(ID: ${data.reversed_journal_entry_id})`
|
||||
);
|
||||
|
||||
// ② 元仕訳内容を取得して修正画面へ遷移
|
||||
const srcRes = await fetch(`${API}/journal-entries/${id}`);
|
||||
if (!srcRes.ok) {
|
||||
alert("元仕訳の取得に失敗しました");
|
||||
return;
|
||||
}
|
||||
|
||||
const srcData = await srcRes.json();
|
||||
localStorage.setItem("editSource", JSON.stringify(srcData));
|
||||
|
||||
// 修正画面を開く
|
||||
window.open("journal-edit.html", "_blank");
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
alert(`エラーが発生しました: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// 仕訳削除
|
||||
// --------------------------------------------------
|
||||
async function deleteJournalEntry(id) {
|
||||
const reason = prompt("削除理由を入力してください:");
|
||||
if (!reason || reason.trim() === "") {
|
||||
alert("削除理由の入力が必要です");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm("この仕訳を削除してもよろしいですか?")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await fetch(`${API}/journal-entries/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ deleted_reason: reason }),
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
alert(data.detail || "削除に失敗しました");
|
||||
return;
|
||||
}
|
||||
|
||||
alert("削除しました");
|
||||
searchJournals(); // リストを再読み込み
|
||||
}
|
||||
|
||||
// ページロード後に前回の検索条件を復元
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
// デフォルト期間を設定(5/31決算:6月1日~現在)
|
||||
const today = new Date();
|
||||
const currentYear = today.getFullYear();
|
||||
const currentMonth = today.getMonth() + 1; // 1-12
|
||||
|
||||
let fiscalStartYear;
|
||||
if (currentMonth >= 6) {
|
||||
// 6月以降:当年6月1日から
|
||||
fiscalStartYear = currentYear;
|
||||
} else {
|
||||
// 1-5月:前年6月1日から
|
||||
fiscalStartYear = currentYear - 1;
|
||||
}
|
||||
|
||||
const defaultFromDate = `${fiscalStartYear}-06-01`;
|
||||
const defaultToDate = today.toISOString().slice(0, 10);
|
||||
|
||||
const savedConditions = localStorage.getItem("journalSearchConditions");
|
||||
if (savedConditions) {
|
||||
const conditions = JSON.parse(savedConditions);
|
||||
if (conditions.fromDate)
|
||||
document.getElementById("searchFromDate").value =
|
||||
conditions.fromDate;
|
||||
else
|
||||
document.getElementById("searchFromDate").value = defaultFromDate;
|
||||
|
||||
if (conditions.toDate)
|
||||
document.getElementById("searchToDate").value = conditions.toDate;
|
||||
else document.getElementById("searchToDate").value = defaultToDate;
|
||||
|
||||
if (conditions.keyword)
|
||||
document.getElementById("searchKeyword").value = conditions.keyword;
|
||||
|
||||
// 自動的に検索を実行
|
||||
searchJournals();
|
||||
} else {
|
||||
// 保存された条件がない場合はデフォルト値を設定
|
||||
document.getElementById("searchFromDate").value = defaultFromDate;
|
||||
document.getElementById("searchToDate").value = defaultToDate;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user