# Attendance Data Merge Examples

This document provides detailed examples of how the attendance sync API merges data intelligently.

## Understanding the Merge Logic

### Rule 1: Time Fields are Additive
If a time field (sign_in, sign_out, lunch times) is **empty** in the database, new data fills it in.
If it already has a value, it is **preserved** (not overwritten).

### Rule 2: Status and Metrics are Always Updated
Fields like status, working_hours, late_minutes are **always updated** when new data provides them.

---

## Example 1: Basic Sign-in/Sign-out Flow

### Initial State
Database is empty for employee "EMP001" on "2026-01-22"

### Sync 1: Morning Sign-in
**Request:**
```json
POST /api/attendance/sync
{
  "employee_id": "EMP001",
  "attendance_date": "2026-01-22",
  "sign_in_time": "08:30:00",
  "sign_in_method": "QR Code",
  "status": "present"
}
```

**Response:**
```json
{
  "status": "success",
  "message": "Attendance synced successfully",
  "action": "created",
  "id": 123
}
```

**Database Record:**
```
id: 123
employee_id: 1
sign_in_time: "08:30:00"
sign_in_method: "QR Code"
sign_out_time: NULL
lunch_out_time: NULL
lunch_in_time: NULL
status: "present"
```

---

### Sync 2: Evening Sign-out
**Request:**
```json
POST /api/attendance/sync
{
  "employee_id": "EMP001",
  "attendance_date": "2026-01-22",
  "sign_out_time": "17:00:00",
  "sign_out_method": "QR Code"
}
```

**Response:**
```json
{
  "status": "success",
  "message": "Attendance record updated with new data",
  "action": "updated",
  "merged_fields": ["sign_out_time", "sign_out_method", "working_hours"]
}
```

**Database Record (After Merge):**
```
id: 123
employee_id: 1
sign_in_time: "08:30:00"        ← PRESERVED from Sync 1
sign_in_method: "QR Code"       ← PRESERVED from Sync 1
sign_out_time: "17:00:00"       ← ADDED from Sync 2
sign_out_method: "QR Code"      ← ADDED from Sync 2
lunch_out_time: NULL
lunch_in_time: NULL
status: "present"               ← PRESERVED from Sync 1
working_hours: 8.50             ← AUTO-CALCULATED
```

---

## Example 2: Complete Day with Lunch Break

### Sync 1: Sign-in (8:30 AM)
```json
{
  "employee_id": "EMP001",
  "attendance_date": "2026-01-22",
  "sign_in_time": "08:30:00",
  "sign_in_method": "Facial Recognition"
}
```
**Database:** `sign_in_time: 08:30:00`

---

### Sync 2: Lunch Start (12:00 PM)
```json
{
  "employee_id": "EMP001",
  "attendance_date": "2026-01-22",
  "lunch_out_time": "12:00:00"
}
```
**Database:**
- `sign_in_time: 08:30:00` ← Preserved
- `lunch_out_time: 12:00:00` ← Added

---

### Sync 3: Lunch End (1:00 PM)
```json
{
  "employee_id": "EMP001",
  "attendance_date": "2026-01-22",
  "lunch_in_time": "13:00:00"
}
```
**Database:**
- `sign_in_time: 08:30:00` ← Preserved
- `lunch_out_time: 12:00:00` ← Preserved
- `lunch_in_time: 13:00:00` ← Added
- `lunch_duration_minutes: 60` ← Auto-calculated

---

### Sync 4: Sign-out (5:00 PM)
```json
{
  "employee_id": "EMP001",
  "attendance_date": "2026-01-22",
  "sign_out_time": "17:00:00",
  "sign_out_method": "Facial Recognition"
}
```
**Final Database State:**
- `sign_in_time: 08:30:00` ← Preserved
- `sign_out_time: 17:00:00` ← Added
- `lunch_out_time: 12:00:00` ← Preserved
- `lunch_in_time: 13:00:00` ← Preserved
- `lunch_duration_minutes: 60` ← Preserved
- `working_hours: 8.50` ← Auto-calculated
- `status: present`

---

## Example 3: Late Arrival with Status Update

### Sync 1: Late Sign-in
```json
{
  "employee_id": "EMP002",
  "attendance_date": "2026-01-22",
  "sign_in_time": "09:15:00",
  "status": "late",
  "late_minutes": 45
}
```
**Database:**
- `sign_in_time: 09:15:00`
- `status: "late"`
- `late_minutes: 45`
- `is_late: 1` ← Auto-set

---

### Sync 2: Sign-out (Manager corrects status)
```json
{
  "employee_id": "EMP002",
  "attendance_date": "2026-01-22",
  "sign_out_time": "18:00:00",
  "status": "present",
  "late_minutes": 0,
  "notes": "Approved late arrival due to traffic"
}
```
**Database:**
- `sign_in_time: 09:15:00` ← Preserved
- `sign_out_time: 18:00:00` ← Added
- `status: "present"` ← UPDATED (was "late")
- `late_minutes: 0` ← UPDATED (was 45)
- `is_late: 0` ← Auto-updated
- `notes: "Approved..."` ← UPDATED
- `working_hours: 8.75` ← Auto-calculated

**Note:** Status and late_minutes are **always updated** fields, so they can be changed in subsequent syncs.

---

## Example 4: Batch Sync with Merge

### Scenario: Syncing Multiple Days
Imagine employee worked Monday-Wednesday, but sync failed. Now syncing all 3 days plus Thursday's sign-in.

**Request:**
```json
POST /api/attendance/sync-batch
{
  "records": [
    {
      "employee_id": "EMP001",
      "attendance_date": "2026-01-20",
      "sign_in_time": "08:30:00",
      "sign_out_time": "17:00:00",
      "working_hours": 8.5
    },
    {
      "employee_id": "EMP001",
      "attendance_date": "2026-01-21",
      "sign_in_time": "08:25:00",
      "sign_out_time": "17:10:00",
      "working_hours": 8.75
    },
    {
      "employee_id": "EMP001",
      "attendance_date": "2026-01-22",
      "sign_in_time": "08:35:00",
      "sign_out_time": "17:00:00",
      "working_hours": 8.42
    },
    {
      "employee_id": "EMP001",
      "attendance_date": "2026-01-23",
      "sign_in_time": "08:30:00"
    }
  ]
}
```

**Response:**
```json
{
  "status": "success",
  "message": "Processed 4 records: 4 created, 0 updated, 0 failed",
  "results": {
    "total": 4,
    "created": 4,
    "updated": 0,
    "failed": 0,
    "errors": []
  }
}
```

All 4 records are created because they don't exist yet.

---

### Later: Thursday Sign-out
**Request:**
```json
POST /api/attendance/sync
{
  "employee_id": "EMP001",
  "attendance_date": "2026-01-23",
  "sign_out_time": "17:05:00"
}
```

**Response:**
```json
{
  "status": "success",
  "message": "Attendance record updated with new data",
  "action": "updated",
  "merged_fields": ["sign_out_time", "working_hours"]
}
```

**Database for 2026-01-23:**
- `sign_in_time: 08:30:00` ← From batch sync
- `sign_out_time: 17:05:00` ← From individual sync (merged)
- `working_hours: 8.58` ← Auto-calculated

---

## Example 5: Handling Duplicate Syncs

### Scenario: Mobile app syncs the same data twice

**First Sync:**
```json
{
  "employee_id": "EMP001",
  "attendance_date": "2026-01-22",
  "sign_in_time": "08:30:00",
  "local_uuid": "abc123"
}
```
**Response:** `"action": "created"`

---

**Second Sync (Duplicate):**
```json
{
  "employee_id": "EMP001",
  "attendance_date": "2026-01-22",
  "sign_in_time": "08:30:00",
  "local_uuid": "abc123"
}
```
**Response:**
```json
{
  "status": "success",
  "message": "Record already exists with all data",
  "action": "skipped"
}
```

Because `sign_in_time` already has the value "08:30:00", there's no new data to merge, so the sync is skipped (but still returns success).

---

## Example 6: Offline Data with Time Conflicts

### Scenario: Employee's offline device has incorrect time

**Database (from accurate sync):**
- `sign_in_time: 08:30:00`

**Offline Sync (with wrong time):**
```json
{
  "employee_id": "EMP001",
  "attendance_date": "2026-01-22",
  "sign_in_time": "08:45:00"
}
```

**Response:**
```json
{
  "status": "success",
  "message": "Record already exists with all data",
  "action": "skipped"
}
```

**Database:**
- `sign_in_time: 08:30:00` ← PRESERVED (not overwritten)

**Why?** Because `sign_in_time` is a **mergeable field**, it only updates if empty. Since it already has a value, the incorrect offline time is ignored.

**Best Practice:** Use `local_uuid` to track which device synced first, or implement a conflict resolution UI.

---

## Example 7: Status and Notes Updates

### Scenario: Manager reviews and updates attendance

**Initial State (from employee sync):**
```
sign_in_time: 09:30:00
sign_out_time: 17:00:00
status: "late"
late_minutes: 60
```

**Manager Update (via API or admin panel):**
```json
{
  "employee_id": "EMP001",
  "attendance_date": "2026-01-22",
  "status": "present",
  "late_minutes": 0,
  "notes": "Medical appointment approved - excused lateness"
}
```

**Final Database:**
```
sign_in_time: 09:30:00        ← Preserved
sign_out_time: 17:00:00       ← Preserved
status: "present"             ← UPDATED
late_minutes: 0               ← UPDATED
is_late: 0                    ← Auto-updated
notes: "Medical..."           ← UPDATED
```

Status and administrative fields are always updated, allowing corrections after the fact.

---

## Summary Table

| Field Type | Merge Behavior | Can Be Updated? | Example |
|-----------|----------------|-----------------|---------|
| Time Fields | Only if empty | First write wins | sign_in_time, lunch_out_time |
| Method Fields | Only if empty | First write wins | sign_in_method, lunch_out_method |
| Status | Always update | Yes, anytime | status |
| Numeric Fields | Always update | Yes, anytime | late_minutes, working_hours |
| Notes | Always update | Yes, anytime | notes |
| Auto-calculated | Recalculated | Automatic | lunch_duration_minutes, working_hours |

---

## Key Takeaways

1. **Time fields are write-once**: First sync wins, subsequent syncs with same field are ignored
2. **Status and metrics are flexible**: Can be updated anytime
3. **Auto-calculations update**: When dependent fields change
4. **No data loss**: Merge ensures all data is preserved, nothing is overwritten unless intentional
5. **Idempotent syncs**: Syncing the same data multiple times is safe

This merge behavior makes the API perfect for:
- Progressive attendance tracking throughout the day
- Offline-first mobile applications
- Multiple sync attempts without data loss
- Partial data collection (e.g., only sign-in, sign-out later)
