# Employee End Day Controller Documentation

**File**: `/controllers/employeeendday.php`  
**Purpose**: Advanced employee shift closure system with attendance tracking, payroll integration, and hierarchical status management  
**Last Updated**: December 20, 2024  
**Total Functions**: 7+  
**Lines of Code**: ~477

---

## 📋 Overview

The Employee End Day Controller is a sophisticated workforce management system that handles the closure of employee shifts with comprehensive attendance tracking, automatic payroll integration, and hierarchical approval workflows. It provides:
- Multi-level shift closure (Employee → Subgroup → Group → Day)
- Real-time attendance data processing with discount calculations
- Automatic payroll system integration via cURL
- Dynamic employee grouping and filtering
- Attendance system integration with configurable rules
- Late arrival and absence tracking with permission management
- Comprehensive audit trail and status tracking

### Primary Functions
- [x] Hierarchical shift closure workflow management
- [x] Real-time attendance data collection and processing
- [x] Automatic late/absence discount calculations
- [x] Integration with payroll system (employeePersonalController)
- [x] Employee grouping and subgroup management
- [x] Dynamic attendance system rule application
- [x] Multi-level status tracking and validation
- [x] Ajax-based real-time operations
- [x] Branch-based access control

### Related Controllers
- [employeePersonalController.php](employeePersonalController.md) - Payroll integration
- [employeegroups.php](employeegroups.md) - Group management
- [employeesubgroups.php](employeesubgroups.md) - Subgroup management
- [employeeAttendanceSystems.php](employeeAttendanceSystems.md) - Attendance rules

---

## 🗄️ Database Tables

### Core Employee Tables
| Table Name | Purpose | Key Columns |
|------------|---------|-------------|
| **employee** | Employee master data | employeeId, employeeName, employeegroupid, employeesubgroupid, branchid, conditions |
| **employeegroup** | Employee group hierarchy | id, name, employeeattendancesystemid, userid, del |
| **employeesubgroup** | Employee subgroup structure | id, name, employeegroupid, employeeattendancesystemid, userid, del |

### Attendance System Tables
| Table Name | Purpose | Key Columns |
|------------|---------|-------------|
| **employeeattendancesystem** | Attendance rules configuration | id, attendanceTime, departureTime, userid, del |
| **employeeclosedayhistory** | Individual employee day closure | id, day, employeeid, attendanceTime, departureTime, latePeriod, lateDiscount, isAbsent, absentDiscount, status |

### Status Tracking Tables
| Table Name | Purpose | Key Columns |
|------------|---------|-------------|
| **employeeclosedaygroupstatus** | Group-level closure status | id, day, employeegroupid, status, userid, sysdate, conditions |
| **employeeclosedaysubgroupstatus** | Subgroup-level closure status | id, day, employeesubgroupid, status, userid, sysdate, conditions |
| **employeeclosedaystatus** | System-wide day closure status | id, day, status, userid, sysdate, conditions |

### Configuration Tables
| Table Name | Purpose | Key Columns |
|------------|---------|-------------|
| **youtubelink** | Tutorial references | youtubelinkid, title, url |

---

## 🔑 Key Functions

### 1. **getdaytoclose** - Load Employee Day Data (Ajax)
**Location**: Line 129-176  
**Purpose**: Load employee data for a specific day with attendance system rules

**Function Signature**:
```php
// Ajax Action: do=getdaytoclose
$day = filter_input(INPUT_POST, 'day');
$employeegroupid = (int) filter_input(INPUT_POST, 'employeegroupid');
$employeesubgroupid = (int) filter_input(INPUT_POST, 'employeesubgroupid');
```

**Process Flow**:
1. **Build Employee Query**:
   ```php
   $queryString = ' WHERE employee.conditions=0 ';
   if ($employeesubgroupid > 0) {
       $queryString .= " and employeesubgroupid = $employeesubgroupid ";
   } elseif ($employeegroupid > 0) {
       $queryString .= " and employeegroupid = $employeegroupid ";
   }
   ```

2. **Apply Branch Filtering**:
   ```php
   if ($_SESSION['branchId'] > 0)
       $queryString .= ' AND  branchid = ' . $_SESSION['branchId'];
   ```

3. **Load Attendance System Rules**:
   ```php
   foreach ($employeeArr as $emp) {
       $attendanceSysData = $employeeAttendanceSystemEX->getAttendanceSystemByEmployeeSubGroup($emp->employeesubgroupid);
       $sys->attendanceTime = $attendanceSysData->attendanceTime;
       $sys->departureTime = $attendanceSysData->departureTime;
       
       // Load employee-specific discount rates
       $sys->halfHourLateWithPermissionDisount = $emp->halfHourLateWithPermissionDisount;
       // ... (all other discount configurations)
   }
   ```

**Features**:
- Dynamic group/subgroup filtering
- Real-time attendance system rule loading
- Employee-specific discount configuration
- JSON-encoded rule data for frontend processing

---

### 2. **closeday** - Execute Day Closure (Ajax)
**Location**: Line 177-315  
**Purpose**: Process and close employee shifts with payroll integration

**Function Signature**:
```php
// Ajax Action: do=closeday
// Transaction-protected operation
$mytransactions = new Transaction();
```

**Process Flow**:
1. **Validate Input Data**:
   ```php
   $day = filter_input(INPUT_POST, 'day');
   $empitr = (int) filter_input(INPUT_POST, 'empitr');
   
   if (isRealDate($day) && $empitr > 1) {
       // Process closure
   }
   ```

2. **Process Each Employee**:
   ```php
   for ($i = 1; $i < $empitr; $i++) {
       $empid = (int) filter_input(INPUT_POST, 'empid' . $i);
       
       $attendanceTime = filter_input(INPUT_POST, 'attendanceTime' . $empid);
       $departureTime = filter_input(INPUT_POST, 'departureTime' . $empid);
       $latePeriod = (float) filter_input(INPUT_POST, 'latePeriod' . $empid);
       $lateDiscount = (float) filter_input(INPUT_POST, 'lateDiscount' . $empid);
       $isAbsent = (int) filter_input(INPUT_POST, 'isAbsent' . $empid);
       $absentDiscount = (float) filter_input(INPUT_POST, 'absentDiscount' . $empid);
   }
   ```

3. **Update Employee History**:
   ```php
   if ($isAbsent == 0) { // Not absent
       $employeeCloseDayHistory->attendanceTime = $attendanceTime . ":00";
       $employeeCloseDayHistory->departureTime = $departureTime . ":00";
       $employeeCloseDayHistory->latePeriod = $latePeriod;
       $employeeCloseDayHistory->lateDiscount = $lateDiscount;
   } elseif ($isAbsent == 1) { // Absent
       $employeeCloseDayHistory->attendanceTime = "00:00:00";
       $employeeCloseDayHistory->departureTime = "00:00:00";
       $employeeCloseDayHistory->isAbsent = $isAbsent;
       $employeeCloseDayHistory->absentDiscount = $absentDiscount;
   }
   ```

4. **Payroll Integration**:
   ```php
   affectOnSalary($employeeCloseDayHistory, $oldEmployeeCloseDayHistory);
   ```

**Hierarchical Status Updates**:
- Updates subgroup status when all employees closed
- Updates group status when all subgroups closed  
- Updates day status when all groups closed

---

### 3. **affectOnSalary()** - Payroll System Integration
**Location**: Line 373-404  
**Purpose**: Integrate attendance data with payroll system via cURL

**Function Signature**:
```php
function affectOnSalary($newEmployeeCloseDayHistory, $oldEmployeeCloseDayHistory)
```

**Process Flow**:
1. **Change Detection**:
   ```php
   if($oldEmployeeCloseDayHistory->status == 1 && 
      $newEmployeeCloseDayHistory->isAbsent == $oldEmployeeCloseDayHistory->isAbsent && 
      $newEmployeeCloseDayHistory->absentDiscount == $oldEmployeeCloseDayHistory->absentDiscount && 
      $newEmployeeCloseDayHistory->lateDiscount == $oldEmployeeCloseDayHistory->lateDiscount) {
       // No changes, do nothing
   }
   ```

2. **Remove Old Salary Effects**:
   ```php
   if ($oldEmployeeCloseDayHistory->status != -1) {
       if ($oldEmployeeCloseDayHistory->isAbsent == 0) {
           curlDeleteEmployeePersonal($oldEmployeeCloseDayHistory->employeeid, 
                                     $oldEmployeeCloseDayHistory->day, 
                                     $oldEmployeeCloseDayHistory->lateDiscount, 5);
       } elseif ($oldEmployeeCloseDayHistory->isAbsent == 1) {
           curlDeleteEmployeePersonal($oldEmployeeCloseDayHistory->employeeid, 
                                     $oldEmployeeCloseDayHistory->day, 
                                     $oldEmployeeCloseDayHistory->absentDiscount, 9);
       }
   }
   ```

3. **Apply New Salary Effects**:
   ```php
   if ($newEmployeeCloseDayHistory->isAbsent == 0) {
       curlAddEmployeePersonal($newEmployeeCloseDayHistory->employeeid, 
                              $newEmployeeCloseDayHistory->day, 
                              $newEmployeeCloseDayHistory->lateDiscount, 5);
   } elseif ($newEmployeeCloseDayHistory->isAbsent == 1) {
       curlAddEmployeePersonal($newEmployeeCloseDayHistory->employeeid, 
                              $newEmployeeCloseDayHistory->day, 
                              $newEmployeeCloseDayHistory->absentDiscount, 9);
   }
   ```

---

### 4. **curlAddEmployeePersonal()** - Add Payroll Entry
**Location**: Line 406-443  
**Purpose**: Add late/absence deductions to employee payroll via cURL

**Function Signature**:
```php
function curlAddEmployeePersonal($empid, $day, $discountVal, $type)
// $type: 5=late, 9=absent
```

**cURL Implementation**:
```php
$post = [
    'fromCtrl' => 'employeeendday',
    'empName' => $empid,
    'empValue' => $discountVal,
    'employeepersonneldate' => $dayAsDateTime,
    'type' => $type, // 5=late, 9=absent
    'Costcenterid' => '-1',
    'paymethod' => '0',
    'userid' => $_SESSION['userid'],
    'saveid' => $_SESSION["saveid"],
    'dbname' => $_SESSION["dbname"],
];

$ch = curl_init('http://localhost/ERP/controllers/employeePersonalController.php?do=add');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
$response = curl_exec($ch);
```

---

### 5. **curlDeleteEmployeePersonal()** - Remove Payroll Entry
**Location**: Line 445-476  
**Purpose**: Remove previously applied payroll deductions via cURL

**Features**:
- Removes late/absence deductions
- Maintains payroll consistency
- Supports editing/reversal operations

---

### 6. **closedayshow** - Historical View
**Location**: Line 316-343  
**Purpose**: Display historical employee day closure data

**Features**:
- Monthly attendance history
- Employee-specific filtering
- Branch-based access control
- Historical trend analysis

---

### 7. **isRealDate()** - Date Validation
**Location**: Line 366-372  
**Purpose**: Validate date format for closure operations

```php
function isRealDate($date) {
    if (false === strtotime($date)) {
        return false;
    }
    list($year, $month, $day) = explode('-', $date);
    return checkdate($month, $day, $year);
}
```

---

## 🔄 Workflows

### Workflow 1: Hierarchical Day Closure Process
```
┌─────────────────────────────────────────────────────────────┐
│                START: Select Day to Close                   │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  1. Load Employee Data for Day                              │
│     - Apply group/subgroup filters                         │
│     - Load attendance system rules                          │
│     - Apply branch restrictions                             │
│     - Generate attendance forms                             │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  2. Process Individual Employee Closures                   │
│     FOR EACH Employee:                                      │
│       │                                                     │
│       ├─→ Record attendance/departure times                 │
│       ├─→ Calculate late period discounts                  │
│       ├─→ Process absence status and discounts             │
│       ├─→ Update employeeclosedayhistory                   │
│       └─→ Integrate with payroll system                    │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  3. Update Subgroup Status                                  │
│     - Mark subgroup as closed                               │
│     - Check if all employees in subgroup are closed        │
│     - Update employeeclosedaysubgroupstatus                 │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  4. Check Group Closure Criteria                           │
│     - Count total subgroups in group                       │
│     - Count closed subgroups for day                       │
│     - IF all subgroups closed: Mark group as closed        │
│     - Update employeeclosedaygroupstatus                    │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  5. Check System-Wide Day Closure                          │
│     - Count total employee groups                          │
│     - Count closed groups for day                          │
│     - IF all groups closed: Mark day as system closed      │
│     - Update employeeclosedaystatus                         │
└─────────────────────────────────────────────────────────────┘
```

---

### Workflow 2: Payroll Integration Process
```
┌─────────────────────────────────────────────────────────────┐
│            START: Employee Attendance Data Changed          │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  1. Detect Changes in Attendance Data                      │
│     - Compare old vs new attendance status                 │
│     - Compare old vs new discount amounts                  │
│     - Determine if payroll update needed                   │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼ (if changes detected)
┌─────────────────────────────────────────────────────────────┐
│  2. Remove Old Payroll Effects                             │
│     IF Previously Late:                                     │
│       │ - cURL DELETE late deduction (type=5)             │
│     IF Previously Absent:                                   │
│       │ - cURL DELETE absence deduction (type=9)          │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  3. Apply New Payroll Effects                              │
│     IF Currently Late:                                      │
│       │ - cURL ADD late deduction (type=5)                │
│     IF Currently Absent:                                   │
│       │ - cURL ADD absence deduction (type=9)             │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  4. Update Employee Personal Records                       │
│     - POST to employeePersonalController.php               │
│     - Include employee ID, date, amount, type              │
│     - Maintain audit trail in payroll system               │
└─────────────────────────────────────────────────────────────┘
```

---

## 🌐 URL Routes & Actions

| URL Parameter | Function Called | Description | Ajax |
|---------------|----------------|-------------|------|
| (empty) or `do=show` | Default display | Show day closure form | No |
| `do=getdaytoclose` | Load day data | Load employees for specific day | Yes |
| `do=closeday` | `closeday()` | Execute day closure process | Yes |
| `do=closedayshow` | Historical view | Show historical closure data | No |
| `do=sucess` | Success page | Display success message | No |
| `do=error` | Error page | Display error message | No |

### Ajax Operations Parameters

**getdaytoclose** (POST):
- `day` - Date to close (YYYY-MM-DD format)
- `employeegroupid` - Group filter (optional)
- `employeesubgroupid` - Subgroup filter (optional)

**closeday** (POST):
- `day` - Date being closed
- `empitr` - Number of employees being processed
- `employeegroupid_day` - Group ID for closure
- `employeesubgroupid_day` - Subgroup ID for closure
- `empid{N}` - Employee ID for each employee
- `attendanceTime{empid}` - Attendance time
- `departureTime{empid}` - Departure time  
- `latePeriod{empid}` - Late period in hours
- `lateDiscount{empid}` - Late discount amount
- `isAbsent{empid}` - Absence status (0/1)
- `absentDiscount{empid}` - Absence discount amount

---

## 🧮 Calculation Methods

### Late Period Discount Calculation
Based on employee-specific rates loaded from attendance system:
```php
// Example discount structure:
$sys->halfHourLateWithPermissionDisount = $emp->halfHourLateWithPermissionDisount;
$sys->hourLateWithPermissionDisount = $emp->hourLateWithPermissionDisount;
$sys->hourAndHalfLateWithPermissionDisount = $emp->hourAndHalfLateWithPermissionDisount;
$sys->twoHoursLateWithPermissionDisount = $emp->twoHoursLateWithPermissionDisount;
$sys->moreThanTwoHoursLateWithPermissionDisount = $emp->moreThanTwoHoursLateWithPermissionDisount;

// Without permission discounts are typically higher
$sys->halfHourLateWithoutPermissionDisount = $emp->halfHourLateWithoutPermissionDisount;
// ... etc
```

### Absence Discount Calculation
```php
// Day absence discounts
$sys->dayAbsenceWithPermissionDisount = $emp->dayAbsenceWithPermissionDisount;
$sys->dayAbsenceWithoutPermissionDisount = $emp->dayAbsenceWithoutPermissionDisount;
```

### Hierarchical Status Logic
```php
// Subgroup closure check
$noOfSubGroupsInaGroup = count($employeeSubGroupEX->queryByEmployeegroupidEX($subGroupData->employeegroupid));
$noOfSubGroupsClosedInaGroupForADay = count($EmployeeclosedaysubgroupstatusEX->queryByEmployeegroupidAndDayGetDistinctSubGroupsClosed($subGroupData->employeegroupid, $day));

if ($noOfSubGroupsInaGroup == $noOfSubGroupsClosedInaGroupForADay) {
    // Mark group as closed
}

// System-wide closure check  
$noOfGroups = $employeeGroupEX->queryAllEXCount();
$noOfGroupsClosedInADay = $employeeCloseDayGroupStatusEX->getNoOfGroupsClosedInADay($day);

if ($noOfGroups == $noOfGroupsClosedInADay) {
    // Mark entire day as closed
}
```

---

## 🔒 Security & Permissions

### Authentication & Session Management
```php
include_once("../public/authentication.php");
```

### Branch-Level Access Control
```php
if ($_SESSION['branchId'] > 0)
    $queryString .= ' AND  branchid = ' . $_SESSION['branchId'];
```

### Transaction Security
```php
$mytransactions = new Transaction();
try {
    // All closure operations
    $mytransactions->commit();
} catch (Exception $e) {
    echo -1;
    $mytransactions->rollback();
}
```

### cURL Security
- Session-based user identification
- Database name validation
- Save ID verification
- Input parameter validation

---

## 🐛 Common Issues & Troubleshooting

### 1. **Payroll Integration Failures**
**Issue**: cURL calls to employeePersonalController.php fail  
**Causes**:
- Incorrect URL (hardcoded localhost)
- Missing session parameters
- Target controller unavailable

**Fix**:
```php
// Use dynamic host configuration
$host = $_SERVER['HTTP_HOST'];
$ch = curl_init("http://$host/ERP/controllers/employeePersonalController.php?do=add");
```

### 2. **Hierarchical Status Issues**
**Issue**: Groups/subgroups not marked as closed properly  
**Debug Steps**:
1. Check employee count queries
2. Verify status update logic
3. Examine closure criteria calculations

### 3. **Date Validation Problems**
**Issue**: Invalid date formats cause closure failures  
**Fix**: Enhance date validation:
```php
function isRealDate($date) {
    if (false === strtotime($date)) {
        return false;
    }
    if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
        return false;
    }
    list($year, $month, $day) = explode('-', $date);
    return checkdate($month, $day, $year);
}
```

### 4. **Ajax Response Handling**
**Issue**: Frontend doesn't receive proper Ajax responses  
**Ensure**:
- Return 1 for success, -1 for error
- No HTML output before Ajax responses
- Proper JSON encoding where needed

---

## 📊 Performance Considerations

### Database Optimization
1. **Critical Indexes**:
   - `employee(employeegroupid, employeesubgroupid, conditions, branchid)`
   - `employeeclosedayhistory(day, employeeid, status)`
   - `employeeclosedaygroupstatus(day, employeegroupid, status)`
   - `employeeclosedaysubgroupstatus(day, employeesubgroupid, status)`

2. **Query Optimization**:
   - Use EXISTS queries for status checks
   - Limit date ranges appropriately
   - Cache attendance system rules

### cURL Performance
- Implement connection pooling
- Use asynchronous calls where possible
- Add timeout configurations
- Cache payroll responses

### Memory Management
- Process employees in batches for large organizations
- Clear variables in loops
- Optimize JSON data structures

---

## 🧪 Testing Scenarios

### Test Case 1: Complete Day Closure Workflow
```
1. Select a day with employees in multiple groups/subgroups
2. Close individual employees with various attendance statuses
3. Verify subgroup closure when all employees closed
4. Check group closure when all subgroups closed  
5. Confirm system-wide closure when all groups closed
6. Validate payroll integration for each step
```

### Test Case 2: Payroll Integration
```
1. Close employee with late arrival
2. Verify late deduction appears in payroll system
3. Edit the same employee's data
4. Confirm old deduction removed and new one applied
5. Test absence scenarios similarly
```

### Test Case 3: Branch Access Control
```
1. Login as branch-restricted user
2. Verify only branch employees appear for closure
3. Test closure operations respect branch boundaries
4. Check status updates don't affect other branches
```

### Test Case 4: Error Handling
```
1. Test with invalid date formats
2. Simulate cURL failures to payroll system
3. Test transaction rollback scenarios
4. Verify data consistency after failures
```

---

## 📚 Related Documentation

- [CLAUDE.md](/Applications/AMPPS/www/erp19/CLAUDE.md) - PHP 8.2 migration guide
- [employeePersonalController.md](employeePersonalController.md) - Payroll integration
- [employeegroups.md](employeegroups.md) - Group management
- [employeeAttendanceSystems.md](employeeAttendanceSystems.md) - Attendance rules

---

**Documented By**: AI Assistant  
**Review Status**: ✅ Complete  
**Next Review**: When major changes occur