本卡屬 FR-143,第 4 棒:延伸 FR-141/142 的 workbook 讀寫。BE 部分依賴第 1 棒,FE 預覽模式依賴第 3 棒。
匯出一份 Excel:「改善計畫」頁只列判不符合的評估目標,一列一個任務;左邊稽核計畫欄和稽核結果欄原樣保留灰底唯讀、同一條評估目標合併儲存格、計畫欄預設收合;右邊每列填執行人員(多人分號分隔,預設原指派人)、到期日、改善措施。PM 填完匯回,先看預覽(哪些會改、哪些對不上),確認後才寫入。範例檔與產生腳本在雛形 prototype/sample/,照它的版面做。
app/flow_control/service/ap_workbook_*.py、ar_workbook_*.py FR-141/142 的 workbook 讀寫與 diff(先看 ar_workbook 怎麼延伸 ap_workbook)
docs/features/FR-143-2610-poam-ao-worksheet-and-excel/prototype/sample/make_sample_xlsx.py 版面契約(合併、群組、凍結、驗證、隱藏欄)
design.md「Excel 契約」節 欄位定義 A~W
FE:第 3 棒 RoundPoamPlanView 的預覽模式(雛形 import-preview.html)
app/flow_control/service/poam_workbook_export.py:沿用 ar_workbook 的四頁(填寫說明、計畫概要、改善計畫、可選清單);改善計畫頁 A part_id(隱藏)、B~J 計畫欄、K~Q 結果欄(兩段灰底、同評估目標合併、D~J column_dimensions.group 收合)、R job_uid(隱藏)、S 任務、T 原指派人(淺藍)、U 執行人員(; 分隔)、V 到期日(date 型別+驗證)、W 改善措施(黃底);評估目標之間粗上框;凍結 S2。可選清單頁只留執行人員(專案參與者)。端點接第 2 棒 …/poam/export?format=xlsx,檔名「改善計畫工作表」。poam_workbook_import.py:POST …/poam-xlsx/parse 讀 A、R 隱藏欄比對(合併儲存格只有左上格有值,其餘列用 A 欄向下填補);U 以分號/全形分號切、trim,名字比對專案參與者(比對法照 ap_workbook_diff._norm);V 接受 date 或 YYYY-MM-DD 字串;輸出 diff {changed:{part_id:{job_uid:{assignees,due,advice,old}}}, problems:{part_id:[...]}}:問題=執行人員不是成員、到期日無法辨識、同一 job_uid 多列 assignees 或 due 不一致、必填空白。存 parse 結果(沿用 ar 的 parse_uid 暫存機制)。POST …/poam-xlsx/<parse_uid>/confirm:body 帶使用者就地修正後的值,走第 2 棒 PUT 的同一支 service 寫入(不重複寫邏輯)。鎖定時 409。src/components/grc/poam/PoamImportPreview.vue:同一頁預覽模式,黃框會更新、紅框待確認就地改,左樹「異動」「待確認」徽章與篩選;全部處理完「確認匯入」才亮(雛形 import-preview.html 的 paintPv 邏輯)。先查再寫:workbook 讀寫、diff、parse 暫存、檔名比對全在 ap_workbook_*/ar_workbook_*,延伸不重寫;合併儲存格讀法 openpyxl ws.merged_cells.ranges。
2026/7/31 → 匯回預覽:黃框兩條、待確認兩條;就地改好 → 確認 → 畫面更新、DB task 值一致。git add <檔名>;append 白話補充(commit hash、手測清單);狀態改「修正待驗證」。push 等令。改 service 後重啟 BE。branch feature/FR-139,不切。