Website : rimsha.abasa.com
backdoor
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
var
/
www
/
mudeerapi.abasa.com
/
nodetest-backup30April26
/
Filename :
TENURE_MIGRATION_SUMMARY.md
back
Copy
# Tenure-Based Leave System Migration ## Summary Successfully migrated from calendar-year `leaveYear` counters on the `user` model to tenure-based tracking in `UserTenure` documents. Leave balances now reset based on each employee's `joined_on` anniversary date. --- ## Changes Made ### 1. **Removed Legacy Fields from User Model** **File:** `src/models/user.models.js` **Removed:** - `paidLeavesUsed` - `unpaidLeavesUsed` - `occasionalOffsUsed` - `leaveYear` **Kept:** - `paidLeavesAllocated` (still used as default for new tenures) - `joined_on` (required for tenure calculation) --- ### 2. **Updated Leave Controller** **File:** `src/controllers/leave.controller.js` #### Removed: - `ensureCurrentYearLeaves()` — no longer needed, tenure ensures year boundaries - `leaveYear` from all API responses (except legacy snapshot in `LeaveRequest.balanceSnapshot`) - Calendar-year reconciliation logic (`reconcileLeaveCountersFromAttendance`) - Separate `migrateToTenures` endpoint #### Added/Updated: - **`migrateToTenureSystem()`** — unified migration that: - Recounts attendance from current tenure's `[startDate, endDate)` - Updates `UserTenure` documents with recounted values - Drops legacy fields (`$unset`) from user documents after migration - Runs automatically on server startup - **`getUserLeaveInfo`** — now returns tenure-based data: ```json { "tenureIndex": 1, "tenureStart": "2026-01-01", "tenureEnd": "2027-01-01", "tenureLabel": "Current Tenure (Jan 1, 2026 – Dec 31, 2026)", "isCurrentTenure": true, "paidLeaves": { "allocated": 12, "used": 6, "remaining": 6 }, "unpaidLeaves": { "used": 0 }, "occasionalOffs": { "used": 4 } } ``` (removed `leaveYear` field) - **`createLeaveRequest`** — now snapshots tenure balance instead of user-level: ```json "balanceSnapshot": { "paidLeavesRemaining": 6, "paidLeavesUsed": 6, "unpaidLeavesUsed": 0, "occasionalOffsUsed": 4, "tenureIndex": 1 } ``` (replaced `leaveYear` with `tenureIndex`) - **`getEmployeeStats`** — removed `leaveYear`, now returns tenure metadata --- ### 3. **Updated Attendance Controller** **File:** `src/controllers/attendance.controller.js` **Changed:** - `reverseLeaveBalance()` now uses `UserTenure` (was updating `user` doc) - Properly resolves tenure from attendance `createdAt` date when reversing/applying leave balance --- ### 4. **Updated Routes** **File:** `src/routes/leave.routes.js` **Removed:** - `POST /user/migrate-to-tenures` (consolidated into single migration) **Kept:** - `POST /user/migrate-leave-data` (now triggers `migrateToTenureSystem`, super_admin only) --- ### 5. **Startup Migration** **File:** `src/index.js` **Changed:** - Now calls `migrateToTenureSystem()` after MongoDB connects - Automatically syncs all active users' current tenure from attendance - Drops legacy user fields after migration - Logs: `"migrateToTenureSystem: synced leave counters to UserTenure for N user(s) (current tenure) and dropped legacy user fields."` --- ### 6. **Updated LeaveRequest Model** **File:** `src/models/leaveRequest.model.js` **Changed `balanceSnapshot`:** ```javascript // Before: balanceSnapshot: { paidLeavesRemaining, paidLeavesUsed, unpaidLeavesUsed, occasionalOffsUsed, leaveYear } // After: balanceSnapshot: { paidLeavesRemaining, paidLeavesUsed, unpaidLeavesUsed, occasionalOffsUsed, tenureIndex } ``` --- ## How It Works Now ### Tenure Calculation - Each user's leave year = `joined_on` → `joined_on + 1 year` (UTC) - Example: - User joined: Jan 15, 2025 - Tenure 1: Jan 15, 2025 – Jan 15, 2026 - Tenure 2: Jan 15, 2026 – Jan 15, 2027 - etc. ### Leave Tracking - **`UserTenure` documents** store: - `paidLeavesUsed`, `unpaidLeavesUsed`, `occasionalOffsUsed` per tenure - `paidLeavesAllocated` (default 12, can be adjusted per tenure) - `startDate`, `endDate` (tenure boundaries) - `tenureIndex` (1-based, incrementing) ### API Behavior - `GET /user/:id/leave-info?month=yyyy-MM` resolves which tenure contains that month - Attendance counts (presents, lates, absents) are scoped to tenure boundaries - Leave balance validation/updates always find the correct tenure from attendance `createdAt` --- ## Migration Safety ✅ **Idempotent:** Safe to run multiple times (on every startup) ✅ **No data loss:** Recounts from attendance (source of truth) ✅ **Automatic cleanup:** Drops legacy fields after sync ✅ **Error handling:** Logs warnings if any user fails, continues with others --- ## Testing Checklist - [x] Remove legacy user fields from schema - [x] Update all leave balance read/write to use UserTenure - [x] Remove `ensureCurrentYearLeaves` calls - [x] Remove `leaveYear` from API responses - [x] Update attendance controller `reverseLeaveBalance` - [x] Consolidate migration into startup script - [x] Test `/user/:id/leave-info` returns tenure data - [x] Test leave request creation snapshots tenure - [x] Test attendance marking updates correct tenure - [ ] **Manual:** Verify frontend handles missing `leaveYear` field - [ ] **Manual:** Test server restart triggers migration - [ ] **Manual:** Verify UserTenure documents created correctly --- ## Rollback Plan (if needed) If you need to revert: 1. Stop the server 2. Restore user schema fields in `user.models.js` 3. Revert `leave.controller.js` to use `ensureCurrentYearLeaves` 4. Remove tenure-based logic from `validateLeaveBalance` / `updateLeaveBalance` 5. Restore `reconcileLeaveCountersFromAttendance` in startup --- ## Notes - **Frontend compatibility:** The frontend may still expect `leaveYear` in responses. Check `AttendanceSummaryCard.tsx` and update if needed. - **Historical data:** Old `LeaveRequest.balanceSnapshot.leaveYear` values remain in DB (won't break anything, just legacy) - **Performance:** Tenure lookups are fast (indexed on `userId` + `tenureIndex`)