314 lines
11 KiB
JavaScript
314 lines
11 KiB
JavaScript
// ============================================================================
|
||
// 【前端集成指南】 - 修正版本追踪系統
|
||
// ============================================================================
|
||
|
||
/**
|
||
* 【API 變更概要】
|
||
*
|
||
* 舊 API:
|
||
* - POST /journal-entries (parent_entry_id) // 創建逆仕訳
|
||
* - 複雜的逆仕訳生成邏輯
|
||
*
|
||
* 新 API:
|
||
* - POST /journal-entries (original_entry_id) // 直接創建修正版本
|
||
* - GET /journal-entries/{id}/revision-history // 查看修正歷史
|
||
*/
|
||
|
||
// ============================================================================
|
||
// 【1】創建新交易(無修正)- 基本用法
|
||
// ============================================================================
|
||
|
||
async function createJournalEntry(entryData) {
|
||
const payload = {
|
||
entry_date: entryData.date, // "2025-01-15"
|
||
description: entryData.description, // "銷售收入"
|
||
lines: entryData.lines, // 交易明細
|
||
tax_paid_account_id: entryData.taxPaidId,
|
||
tax_received_account_id: entryData.taxReceivedId,
|
||
rounding_mode: "round"
|
||
// 注意:這裡 original_entry_id 沒有指定,表示這是新交易
|
||
};
|
||
|
||
const response = await fetch("/journal-entries", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify(payload)
|
||
});
|
||
|
||
if (response.ok) {
|
||
const result = await response.json();
|
||
console.log("✓ 交易創建成功:", result.journal_entry_id);
|
||
return result;
|
||
} else {
|
||
alert("❌ 創建失敗");
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 【2】創建修正版本 - 關鍵變更
|
||
// ============================================================================
|
||
|
||
async function createRevisionEntry(originalEntryId, entryData) {
|
||
const payload = {
|
||
entry_date: entryData.date,
|
||
description: `【修正】${entryData.description}`, // 添加【修正】標記
|
||
lines: entryData.lines,
|
||
tax_paid_account_id: entryData.taxPaidId,
|
||
tax_received_account_id: entryData.taxReceivedId,
|
||
rounding_mode: "round",
|
||
|
||
// ✨ 新參數:指向原始交易
|
||
original_entry_id: originalEntryId
|
||
};
|
||
|
||
const response = await fetch("/journal-entries", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify(payload)
|
||
});
|
||
|
||
if (response.ok) {
|
||
const result = await response.json();
|
||
console.log("✓ 修正版本創建成功:");
|
||
console.log(` 原始交易 ID: ${originalEntryId}`);
|
||
console.log(` 新版本 ID: ${result.journal_entry_id}`);
|
||
console.log(` 版本: ${result.revision_created ? "新版本" : "覆蓋"}`);
|
||
return result;
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 【3】查看修正歷史 - 新功能
|
||
// ============================================================================
|
||
|
||
async function viewRevisionHistory(journalId) {
|
||
const response = await fetch(`/journal-entries/${journalId}/revision-history`);
|
||
|
||
if (response.ok) {
|
||
const data = await response.json();
|
||
|
||
console.log(`修正歷史 (原始交易 ID: ${data.original_entry_id}):`);
|
||
console.log(`總版本數: ${data.total_versions}`);
|
||
|
||
data.versions.forEach(version => {
|
||
const status = version.is_latest ? "【最新】" : "【已過時】";
|
||
console.log(`
|
||
版本 ${version.revision_count}: ${status}
|
||
- 交易 ID: ${version.journal_entry_id}
|
||
- 摘要: ${version.description}
|
||
- 建立時間: ${version.created_at}
|
||
`);
|
||
});
|
||
|
||
return data;
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 【4】前端修正流程 - 完整示例
|
||
// ============================================================================
|
||
|
||
async function handleEditTransaction(originalEntryId) {
|
||
// 步驟 1: 顯示修正對話框
|
||
const originalData = await fetchJournalEntry(originalEntryId); // 現有的 API
|
||
|
||
const editedData = showEditDialog(originalData); // 用户編緬
|
||
|
||
if (!editedData) {
|
||
console.log("⚠️ 編輯已取消");
|
||
return;
|
||
}
|
||
|
||
// 步驟 2: 檢查是否有實際變更
|
||
if (areEntriesEqual(originalData, editedData)) {
|
||
console.log("⚠️ 沒有任何變更");
|
||
return;
|
||
}
|
||
|
||
// 步驟 3: 創建修正版本
|
||
const result = await createRevisionEntry(originalEntryId, editedData);
|
||
|
||
if (result && result.status === "ok") {
|
||
// 步驟 4: 更新頁面
|
||
refreshJournalList(); // 重新加載列表
|
||
alert(`✓ 交易已修正。新版本 ID: ${result.journal_entry_id}`);
|
||
|
||
// 步驟 5: 可選 - 顯示修正歷史
|
||
// await showRevisionHistory(originalEntryId);
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 【5】試算表查詢 - 前端優化
|
||
// ============================================================================
|
||
|
||
/**
|
||
* 舊做法(不需要):
|
||
* const entries = await fetchAllJournalEntries();
|
||
* const latestOnly = entries.filter(e => e.parent_entry_id === null);
|
||
*
|
||
* 新做法(推薦):
|
||
* 後端已經自動過濾, 返回的都是 is_latest = true 的交易
|
||
*/
|
||
|
||
async function fetchTrialBalance(fromDate, toDate) {
|
||
// 不需要任何客戶端過濾邏輯
|
||
// 後端已確保只返回 is_latest = true 的交易
|
||
|
||
const response = await fetch(
|
||
`/journal-entries?from_date=${fromDate}&to_date=${toDate}`,
|
||
{ method: "GET" }
|
||
);
|
||
|
||
if (response.ok) {
|
||
const entries = await response.json();
|
||
|
||
// entries 已經只包含最新版本
|
||
// 直接計算試算表
|
||
return calculateTrialBalance(entries);
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 【6】修正歷史界面 - UI 示例
|
||
// ============================================================================
|
||
|
||
function displayRevisionHistory(journalId) {
|
||
// HTML 結構
|
||
const html = `
|
||
<div class="revision-history">
|
||
<h3>修正歷史</h3>
|
||
|
||
<div class="revision-timeline" id="timeline">
|
||
<!-- 將使用 JavaScript 動態填充 -->
|
||
</div>
|
||
|
||
<button onclick="showRevisionComparison()">
|
||
對比版本
|
||
</button>
|
||
</div>
|
||
`;
|
||
|
||
// 獲取歷史數據
|
||
viewRevisionHistory(journalId).then(data => {
|
||
const timeline = document.getElementById("timeline");
|
||
|
||
data.versions.forEach(version => {
|
||
const element = document.createElement("div");
|
||
element.className = `revision-item ${version.is_latest ? "latest" : "outdated"}`;
|
||
element.innerHTML = `
|
||
<div class="revision-header">
|
||
<span class="version-number">版本 ${version.revision_count}</span>
|
||
<span class="status">
|
||
${version.is_latest ? "✓ 最新" : "過時"}
|
||
</span>
|
||
</div>
|
||
<div class="revision-details">
|
||
<p><strong>摘要:</strong> ${version.description}</p>
|
||
<p><strong>日期:</strong> ${version.entry_date}</p>
|
||
<p><strong>建立:</strong> ${new Date(version.created_at).toLocaleString()}</p>
|
||
<p><strong>操作員:</strong> ${version.created_by}</p>
|
||
</div>
|
||
`;
|
||
timeline.appendChild(element);
|
||
});
|
||
});
|
||
}
|
||
|
||
// ============================================================================
|
||
// 【7】向後兼容性 - 舊 parent_entry_id 支持
|
||
// ============================================================================
|
||
|
||
/**
|
||
* 為了向後兼容,後端仍然接受 parent_entry_id 參數
|
||
* 但將被忽略,請改用 original_entry_id
|
||
*
|
||
* 舊代碼仍然有效(會自動轉換):
|
||
*/
|
||
|
||
async function createRevisionParentMethod(parentId, data) {
|
||
// ⚠️ 不推薦 - 舊方式
|
||
return await fetch("/journal-entries", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({
|
||
...data,
|
||
parent_entry_id: parentId // ← 舊參數(向後兼容)
|
||
})
|
||
});
|
||
}
|
||
|
||
// ============================================================================
|
||
// 【8】錯誤處理
|
||
// ============================================================================
|
||
|
||
async function createRevisionWithErrorHandling(originalEntryId, data) {
|
||
try {
|
||
const result = await createRevisionEntry(originalEntryId, data);
|
||
|
||
if (!result.status) {
|
||
throw new Error("API 響應格式錯誤");
|
||
}
|
||
|
||
if (result.status === "error") {
|
||
throw new Error(result.detail || "未知錯誤");
|
||
}
|
||
|
||
return result;
|
||
|
||
} catch (error) {
|
||
console.error("❌ 修正失敗:", error.message);
|
||
alert(`修正失敗: ${error.message}`);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 【9】測試檢查清單
|
||
// ============================================================================
|
||
|
||
/**
|
||
* 部署前請確認以下功能正常:
|
||
*
|
||
* [ ] 創建新交易 (original_entry_id = null)
|
||
* [ ] 創建修正版本 (original_entry_id ≠ null)
|
||
* [ ] 試算表只顯示最新版本
|
||
* [ ] 修正歷史端點返回完整列表
|
||
* [ ] 舊的 parent_entry_id 參數仍然可用(向後兼容)
|
||
* [ ] 修改同一交易三次,全部版本都能查詢
|
||
*/
|
||
|
||
// ============================================================================
|
||
// 【10】遷移檢查表 - 前端
|
||
// ============================================================================
|
||
|
||
const PRE_DEPLOYMENT_CHECKLIST = {
|
||
API_Changes: [
|
||
"✓ 使用 original_entry_id 創建修正版本",
|
||
"✓ 調用 GET .../revision-history 查看歷史",
|
||
"✓ 試算表查詢已包含 is_latest = true(後端處理)"
|
||
],
|
||
|
||
UI_Updates: [
|
||
"[ ] 修正的交易不再出現在主列表中",
|
||
"[ ] 添加"查看歷史"按鈕",
|
||
"[ ] 添加修正時間線顯示",
|
||
"[ ] 錯誤提示信息已更新"
|
||
],
|
||
|
||
Testing: [
|
||
"[ ] 單元測試已更新",
|
||
"[ ] 集成測試已完成",
|
||
"[ ] E2E 測試已通過",
|
||
"[ ] 性能測試已驗證"
|
||
],
|
||
|
||
Documentation: [
|
||
"[ ] 用户文檔已更新",
|
||
"[ ] API 文檔已更新",
|
||
"[ ] 訓練資料已準備"
|
||
]
|
||
};
|
||
|
||
// ============================================================================
|