Schema:
- attendance_records: last_edited_at + last_edited_by (was: is_manually_edited boolean only)
- accidents: last_edited_at + last_edited_by (was: no edit tracking)
Migration: ALTER TABLE adds columns (idempotent)
Logic:
- Attendance import: skip if last_edited_at IS NOT NULL OR is_manually_edited (backward compat)
- Accident import: skip if last_edited_at IS NOT NULL (was: always overwrite)
PUT endpoints:
- PUT /api/attendance/{record_id}: set last_edited_at + last_edited_by + is_manually_edited
- PUT /api/accident/{record_id} (NEW): set last_edited_at + last_edited_by, whitelist editable fields
Verified via direct _import_attendance_to_db / _import_accident_to_db call:
- Edited attendance: skipped on re-import (status_text preserved)
- Edited accident: skipped on re-import (description preserved)
- Not-edited accident: updated to new values (description changed)
Note: HTTP /api/upload/{section} returned 404 in test, but function logic verified working
- Add last_edited_at + last_edited_by columns to holidays + leave_records
- Migration: ALTER TABLE adds columns (idempotent)
- PUT endpoints set last_edited_at on every manual edit
- POST /api/leave/upload: skip if last_edited_at NOT NULL, update otherwise
- New POST /api/holidays/upload: same logic, admin only
- LeaveOut + HolidayOut schemas include last_edited_at
- Response includes added/updated/skipped counters
- Verified: edited Jay Lam SL preserved, not-edited May Chan SL updated