# HR 管理試算表專案

## Python 執行指令
在此專案中執行 Python 腳本時，請使用以下指令：
```bash
python3 scripts/script_name.py
```

## 安裝 Python 套件
安裝套件時請使用：
```bash
pip install --break-system-packages package_name
```

## 環境設定
- Google Sheets API 憑證設定在 service_account_key.json 檔案中

## Python 工具腳本（`scripts/` 目錄）

### `scripts/compare_sheets.py`
比較梁丹妮試算表的「2026年加班登記表（選擇補休）」和「2026年加班登記表」兩張表的內容，用來驗證 GAS 填表結果是否正確。
```bash
python3 scripts/compare_sheets.py
```

### `scripts/check_execution_log.py`
讀取**主控台試算表**（ID: `12dg8UjMTUh8SFPS-X4v83aIwNUEm5CYEKO3RNUW7QCA`）的工作表清單與執行紀錄，用來查看系統執行狀況。
```bash
python3 scripts/check_execution_log.py
```

---

## Google Apps Script 專案

- 使用 **clasp** 管理 Google Apps Script 程式碼
- 主要 API：SpreadsheetApp
- 程式碼入口：`Code.gs` / `src/` 目錄

## 效能規範

- **必須批次讀寫**：使用 `getValues()` / `setValues()`，禁止逐筆 `getValue()` / `setValue()`
- 每位員工處理完才記錄一次進度，不要每筆記錄都更新

## 安全規範

- 所有寫入操作前須有備份機制
- 關鍵操作加 `try-catch`，失敗時不影響其他員工的處理

## ID 格式

- 加班編號：`OT-YYYYMMDD-員工編號-流水號`，例：`OT-20250801-E001-1`
- 補休編號：`LV-YYYYMMDD-員工編號-流水號`，例：`LV-20250815-E001-1`

## 程式碼結構（Code.gs）

```javascript
// ===== 主要流程 =====
function checkAllEmployees() { }

// ===== 員工資料檢查 =====
function checkEmployeeData(employeeId, fileId) { }
function scanMonthlySheets(fileId) { }
function extractOvertimeData(sheet) { }
function addOvertimeRecord(record) { }

// ===== 反向驗證 =====
function validateOvertimeRecords() { }

// ===== 補休配對 =====
function matchLeaveWithOvertime() { }
function allocateLeaveToOvertime(leaveRecord) { }
function updateOvertimeStatus(overtimeId) { }

// ===== 工具函數 =====
function getMasterSheet(sheetName) { }
function getEmployeeSheet(fileId, sheetName) { }
function generateOvertimeId(date) { }
function logError(type, employeeId, message) { }
```

## 錯誤記錄格式

工作表「執行紀錄」欄位：
```
| 時間戳記 | 錯誤類型 | 員工編號 | 詳細訊息 | 受影響資料 |
```

---

## OvertimeFill.js（梁丹妮填表）

**試算表 ID：** `1Y708RZgZHL3Gqah9ujiw5brMuxAoW3RFl0txfXq8qrE`

**寫入目標：**
- `2026年加班登記表`（OT 登記，含各月合計列、橘色 `#fce5cd`）
- `2026年加班選擇補休登記表`（補休記錄與月後剩餘）

**⚠️ 不可讀寫：**
- `2026年加班登記表（選擇補休）` — 此為人工手算的比對用參考表，程式不應碰它

### 資料來源

| 資料 | 來源 | 條件 |
|------|------|------|
| 加班記錄 | 月份分頁 `2026.XX` | L 欄（加班時數）> 0 |
| 補休記錄 | 月份分頁 `2026.XX` | D 欄（屬性）=== `'補休'`（注意：`'國定假日的補休日'` 不算） |

### 月份分頁欄位（A~O）
```
A=員工  B=日期  C=星期  D=屬性  E=上班打卡  F=下班打卡
G=應上下班時間  H=工作時數  I=午晚休時數
J=扣除午晚休後工時  K=實際工時扣除應上班工時  L=加班時數
M=備註  N=選擇請領加班費  O=選擇補休
```

**補休時數**：優先取 J 欄（float，小時），J 為空則用 F-E 計算。

### 月份動態掃描
`getMonths_(ss)` 自動找試算表中所有符合 `/^2026\.\d{2}$/` 的分頁，不需硬編碼。

### GAS 時間解析注意事項
`cellToHHMM_()` 處理 `Date` 物件時，**必須用 `val.getUTCHours()` / `val.getUTCMinutes()`**，禁止用：
- `getHours()` / `getMinutes()`（依 GAS 伺服器時區，不可靠）
- `Utilities.formatDate(val, 'Asia/Taipei', ...)`（⚠️ 錯誤！Java timezone DB 對 1899-12-30 套用歷史 LMT，造成輸出偏移 +23 分鐘）

**根本原因**：GAS 時間格回傳的 Date 物件，UTC 時間直接等於儲存格顯示值（Dec 30, 1899 為基準）。例如格子顯示 18:30 → Date 的 UTC 時間就是 18:30。因此 `getUTCHours()` = 18, `getUTCMinutes()` = 30 ✓。`formatDate('Asia/Taipei', ...)` 則因歷史 LMT 偏移造成 18:53（多 23 分）。