can('manage-employee-salaries')) { // Auto-create salary records for employees who don't have one $companyEmployees = User::with('employee') ->where('type', 'employee') ->whereIn('created_by', getCompanyAndUsersId()) ->where('status', 'active') ->get(); // if (Auth::user()->can('manage-any-employee-salaries')) { // foreach ($companyEmployees as $employee) { // $exists = EmployeeSalary::where('employee_id', $employee->id)->exists(); // if (!$exists) { // EmployeeSalary::create([ // 'employee_id' => $employee->id, // 'basic_salary' => $employee->employee?->base_salary ?? 0, // 'components' => null, // 'is_active' => true, // 'created_by' => creatorId(), // ]); // } else { // if (is_null($employee->employee->base_salary)) { // // If base salary is null in employee table then it will update the employee salary in employee table // $getEmployeeBaseSalary = EmployeeSalary::where('employee_id', $employee->employee->user_id)->first(); // if ($getEmployeeBaseSalary) { // $employee->employee->base_salary = $getEmployeeBaseSalary->basic_salary; // $employee->employee->save(); // } // } else { // // If salary update on employee table it will automatically affect on Employee salary table // $getEmployeeBaseSalary = EmployeeSalary::where('employee_id', $employee->employee->user_id)->first(); // if ($getEmployeeBaseSalary) { // $getEmployeeBaseSalary->basic_salary = $employee->employee->base_salary; // $getEmployeeBaseSalary->save(); // } // } // } // } // } if (Auth::user()->can('manage-any-employee-salaries')) { foreach ($companyEmployees as $employee) { // Safety check: employee relation must exist if (!isset($employee->employee)) { continue; } $employeeModel = $employee->employee; // Fetch salary record once $employeeSalary = EmployeeSalary::where('employee_id', $employee->id)->first(); // If salary record does not exist → create if (!$employeeSalary) { EmployeeSalary::create([ 'employee_id' => $employee->id, 'basic_salary' => $employeeModel->base_salary ?? 0, 'pay_frequency' => 'monthly', 'components' => null, 'is_active' => true, 'created_by' => creatorId(), ]); continue; } // If base_salary is NULL in employee table → update employee table if (is_null($employeeModel->base_salary)) { if (!is_null($employeeSalary->basic_salary)) { $employeeModel->base_salary = $employeeSalary->basic_salary; $employeeModel->save(); } } // If base_salary exists → update salary table else { if ($employeeSalary->basic_salary != $employeeModel->base_salary) { $employeeSalary->basic_salary = $employeeModel->base_salary; $employeeSalary->save(); } } } } $query = EmployeeSalary::with(['employee', 'creator'])->where(function ($q) { if (Auth::user()->can('manage-any-employee-salaries')) { $q->whereIn('created_by', getCompanyAndUsersId()); } elseif (Auth::user()->can('manage-own-employee-salaries')) { $q->where('created_by', Auth::id())->orWhere('employee_id', Auth::id())->where('is_active', 1); } else { $q->whereRaw('1 = 0'); } }); // Handle search if ($request->has('search') && !empty($request->search)) { $query->where(function ($q) use ($request) { $q->whereHas('employee', function ($subQ) use ($request) { $subQ->where('name', 'like', '%' . $request->search . '%'); }); }); } // Handle employee filter if ($request->has('employee_id') && !empty($request->employee_id) && $request->employee_id !== 'all') { $query->where('employee_id', $request->employee_id); } // Handle active status filter if ($request->has('is_active') && !empty($request->is_active) && $request->is_active !== 'all') { $query->where('is_active', $request->is_active === 'active'); } // Handle sorting if ($request->has('sort_field') && !empty($request->sort_field)) { $sortField = $request->sort_field; $sortDirection = $request->sort_direction ?? 'asc'; if ($sortField === 'basic_salary') { $query->orderBy('basic_salary', $sortDirection); } else { $query->orderBy('id', 'desc'); } } else { $query->orderBy('id', 'desc'); } $employeeSalaries = $query->paginate($request->per_page ?? 10); // Load component names and types for each salary record $employeeSalaries->getCollection()->transform(function ($salary) { if ($salary->components) { $components = SalaryComponent::whereIn('id', $salary->components) ->get(['id', 'name', 'type']); $salary->component_names = $components->pluck('name')->toArray(); $salary->component_types = $components->pluck('type')->toArray(); } else { $salary->component_names = []; $salary->component_types = []; } if ($salary->employee) { $rawAvatar = $salary->employee->getRawOriginal('avatar'); $salary->employee->avatar = check_file($rawAvatar) ? get_file($rawAvatar) : get_file('avatars/avatar.png'); } return $salary; }); // Get employees for filter dropdown $employees = User::where('type', 'employee') ->whereIn('created_by', getCompanyAndUsersId()) ->get(['id', 'name']); // Get salary components for form $salaryComponents = SalaryComponent::where('status', 'active') ->whereIn('created_by', getCompanyAndUsersId()) ->get(['id', 'name', 'type', 'calculation_type', 'default_amount', 'percentage_of_basic']); return Inertia::render('hr/employee-salaries/index', [ 'employeeSalaries' => $employeeSalaries, 'employees' => $this->getFilteredEmployees(), 'salaryComponents' => $salaryComponents, 'filters' => $request->all(['search', 'employee_id', 'is_active', 'sort_field', 'sort_direction', 'per_page']), ]); } else { return redirect()->back()->with('error', __('Permission Denied.')); } } private function getFilteredEmployees() { // Get employees for filter dropdown (compatible with getFilteredEmployees logic) $employeeQuery = Employee::whereIn('created_by', getCompanyAndUsersId()); if (Auth::user()->can('manage-own-employee-salaries') && !Auth::user()->can('manage-any-employee-salaries')) { $employeeQuery->where(function ($q) { $q->where('created_by', Auth::id())->orWhere('user_id', Auth::id()); }); } $employees = User::emp() ->with('employee') ->whereIn('created_by', getCompanyAndUsersId()) ->where('status', 'active') ->whereIn('id', $employeeQuery->pluck('user_id')) ->select('id', 'name') ->get() ->map(function ($user) { return [ 'id' => $user->id, 'name' => $user->name, 'employee_id' => $user->employee->employee_id ?? '', ]; }); return $employees; } public function store(Request $request) { $validated = $request->validate([ 'employee_id' => 'required|exists:users,id', 'basic_salary' => 'required|numeric|min:0', 'pay_frequency' => 'required|in:monthly,semi-monthly,weekly,daily', 'components' => 'nullable|array', 'components.*' => 'exists:salary_components,id', 'sss_fixed' => 'nullable|numeric|min:0', 'philhealth_fixed' => 'nullable|numeric|min:0', 'pagibig_fixed' => 'nullable|numeric|min:0', 'payroll_type' => 'nullable|in:automatic,manual', 'fixed_overtime' => 'nullable|numeric|min:0', 'fixed_holiday_pay' => 'nullable|numeric|min:0', 'fixed_late_deduction' => 'nullable|numeric|min:0', 'is_time_exempt' => 'boolean|nullable', 'notes' => 'nullable|string', ]); // Check if employee already has salary $exists = EmployeeSalary::where('employee_id', $validated['employee_id']) ->whereIn('created_by', getCompanyAndUsersId()) ->exists(); if ($exists) { return redirect()->back()->with('error', __('Employee already has a salary record. Please update the existing one.')); } $validated['created_by'] = creatorId(); $validated['is_active'] = true; EmployeeSalary::create($validated); // Sync with core employee profile immediately so it doesn't revert on index reload if (isset($validated['basic_salary'])) { $employeeModel = \App\Models\Employee::where('user_id', $validated['employee_id'])->first(); if ($employeeModel) { $employeeModel->base_salary = $validated['basic_salary']; $employeeModel->save(); } } return redirect()->back()->with('success', __('Employee salary created successfully.')); } public function update(Request $request, $employeeSalaryId) { $employeeSalary = EmployeeSalary::where('id', $employeeSalaryId) ->whereIn('created_by', getCompanyAndUsersId()) ->first(); if ($employeeSalary) { try { $validated = $request->validate([ 'employee_id' => 'required|exists:users,id', 'basic_salary' => 'required|numeric|min:0', 'pay_frequency' => 'required|in:monthly,semi-monthly,weekly,daily', 'components' => 'nullable|array', 'components.*' => 'exists:salary_components,id', 'sss_fixed' => 'nullable|numeric|min:0', 'philhealth_fixed' => 'nullable|numeric|min:0', 'pagibig_fixed' => 'nullable|numeric|min:0', 'payroll_type' => 'nullable|in:automatic,manual', 'fixed_overtime' => 'nullable|numeric|min:0', 'fixed_holiday_pay' => 'nullable|numeric|min:0', 'fixed_late_deduction' => 'nullable|numeric|min:0', 'is_active' => 'boolean', 'is_time_exempt' => 'boolean|nullable', 'notes' => 'nullable|string', ]); $employeeSalary->update($validated); // Sync with core employee profile immediately so it doesn't revert on index reload if (isset($validated['basic_salary'])) { $employeeModel = \App\Models\Employee::where('user_id', $validated['employee_id'])->first(); if ($employeeModel) { $employeeModel->base_salary = $validated['basic_salary']; $employeeModel->save(); } } return redirect()->back()->with('success', __('Employee salary updated successfully')); } catch (\Exception $e) { return redirect()->back()->with('error', $e->getMessage() ?: __('Failed to update employee salary')); } } else { return redirect()->back()->with('error', __('Employee salary Not Found.')); } } public function destroy($employeeSalaryId) { $employeeSalary = EmployeeSalary::where('id', $employeeSalaryId) ->whereIn('created_by', getCompanyAndUsersId()) ->first(); if ($employeeSalary) { try { $employeeSalary->delete(); return redirect()->back()->with('success', __('Employee salary deleted successfully')); } catch (\Exception $e) { return redirect()->back()->with('error', $e->getMessage() ?: __('Failed to delete employee salary')); } } else { return redirect()->back()->with('error', __('Employee salary Not Found.')); } } public function toggleStatus($employeeSalaryId) { $employeeSalary = EmployeeSalary::where('id', $employeeSalaryId) ->whereIn('created_by', getCompanyAndUsersId()) ->first(); if ($employeeSalary) { try { $employeeSalary->is_active = !$employeeSalary->is_active; $employeeSalary->save(); return redirect()->back()->with('success', __('Employee salary status updated successfully')); } catch (\Exception $e) { return redirect()->back()->with('error', $e->getMessage() ?: __('Failed to update employee salary status')); } } else { return redirect()->back()->with('error', __('Employee salary Not Found.')); } } public function showPayroll($employeeSalaryId) { if (Auth::user()->can('manage-employee-salaries')) { try { // $employeeSalary = EmployeeSalary::where('id', $employeeSalaryId) // ->whereIn('created_by', getCompanyAndUsersId()) // ->with('employee') // ->first(); $employeeSalary = EmployeeSalary::with(['employee']) ->where('id',$employeeSalaryId) ->where(function ($q) { if (Auth::user()->can('manage-any-employee-salaries')) { $q->whereIn('created_by', getCompanyAndUsersId()); } elseif (Auth::user()->can('manage-own-employee-salaries')) { $q->where('created_by', Auth::id())->orWhere('employee_id', Auth::id())->where('is_active', 1); } else { $q->whereRaw('1 = 0'); } })->first(); if (!$employeeSalary) { return redirect()->route('hr.employee-salaries.index') ->with('error', __('Employee salary record not found.')); } // Get payroll runs for this employee $payrollRuns = \App\Models\PayrollRun::whereIn('created_by', getCompanyAndUsersId()) ->whereHas('payrollEntries', function ($query) use ($employeeSalary) { $query->where('employee_id', $employeeSalary->employee_id); }) ->orderBy('pay_period_end', 'desc') ->get(['id', 'title', 'pay_period_start', 'pay_period_end', 'status']); if ($payrollRuns->isEmpty()) { return redirect()->route('hr.employee-salaries.index') ->with('error', __('No payroll runs found for this employee.')); } // Get the latest payroll run $latestPayrollRun = $payrollRuns->first(); return Inertia::render('hr/employee-salaries/payroll-calculation', [ 'employeeSalary' => $employeeSalary, 'payrollRuns' => $payrollRuns, 'selectedPayrollRun' => $latestPayrollRun, 'payrollData' => $this->getPayrollCalculationData($employeeSalary, $latestPayrollRun) ]); } catch (\Exception $e) { return redirect()->route('hr.employee-salaries.index') ->with('error', __('Failed to load payroll calculation.')); } } else { return redirect()->back()->with('error', __('Permission Denied.')); } } public function getPayrollCalculation($employeeSalaryId, $payrollRunId) { try { $employeeSalary = EmployeeSalary::where('id', $employeeSalaryId) ->whereIn('created_by', getCompanyAndUsersId()) ->with('employee') ->first(); $payrollRun = \App\Models\PayrollRun::where('id', $payrollRunId) ->whereIn('created_by', getCompanyAndUsersId()) ->first(); if (!$employeeSalary || !$payrollRun) { return response()->json(['error' => 'Record not found'], 404); } $payrollData = $this->getPayrollCalculationData($employeeSalary, $payrollRun); return response()->json($payrollData); } catch (\Exception $e) { return response()->json(['error' => 'Failed to calculate payroll'], 500); } } private function getPayrollCalculationData($employeeSalary, $payrollRun) { // Get payroll entry for this employee and payroll run $payrollEntry = \App\Models\PayrollEntry::where('employee_id', $employeeSalary->employee_id) ->where('payroll_run_id', $payrollRun->id) ->first(); if (!$payrollEntry) { return [ 'payrollEntry' => null, 'salaryBreakdown' => ['earnings' => [], 'deductions' => []], 'attendanceSummary' => [], 'payrollCalculation' => ['net_salary' => 0, 'total_earnings' => 0, 'total_deductions' => 0], 'attendanceRecords' => [] ]; } // Get attendance records for the payroll period $attendanceRecords = \App\Models\AttendanceRecord::where('employee_id', $employeeSalary->employee_id) ->whereBetween('date', [$payrollRun->pay_period_start, $payrollRun->pay_period_end]) ->orderBy('date') ->get(); // Calculate attendance summary from payroll entry $attendanceSummary = [ 'total_working_days' => $payrollEntry->working_days, 'present_days' => $payrollEntry->present_days, 'absent_days' => $payrollEntry->absent_days, 'half_days' => $payrollEntry->half_days, 'leave_days' => $payrollEntry->paid_leave_days, 'holiday_days' => $payrollEntry->holiday_days, 'overtime_hours' => $payrollEntry->overtime_hours, 'unpaid_leave_days' => $payrollEntry->unpaid_leave_days, 'unpaid_leave_from_leave' => $payrollEntry->unpaid_leave_days - $payrollEntry->absent_days - ($payrollEntry->half_days * 0.5) ]; // Get salary breakdown from payroll entry $salaryBreakdown = [ 'earnings' => is_array($payrollEntry->earnings_breakdown) ? $payrollEntry->earnings_breakdown : json_decode($payrollEntry->earnings_breakdown ?? '{}', true), 'deductions' => is_array($payrollEntry->deductions_breakdown) ? $payrollEntry->deductions_breakdown : json_decode($payrollEntry->deductions_breakdown ?? '{}', true) ]; $payrollCalculation = [ 'net_salary' => $payrollEntry->net_pay, 'total_earnings' => $payrollEntry->total_earnings, 'total_deductions' => $payrollEntry->total_deductions, 'per_day_salary' => $payrollEntry->per_day_salary ?? 0, 'overtime_amount' => $payrollEntry->overtime_amount ?? 0 ]; return [ 'payrollEntry' => $payrollEntry, 'salaryBreakdown' => $salaryBreakdown, 'attendanceSummary' => $attendanceSummary, 'payrollCalculation' => $payrollCalculation, 'attendanceRecords' => $attendanceRecords, 'currentMonth' => $payrollRun->pay_period_end ]; } private function calculateAttendanceSummary($attendanceRecords, $payrollRun) { $summary = [ 'total_working_days' => 0, 'present_days' => 0, 'absent_days' => 0, 'half_days' => 0, 'leave_days' => 0, 'holiday_days' => 0, 'overtime_hours' => 0, 'unpaid_leave_days' => 0, 'unpaid_leave_from_leave' => 0 ]; foreach ($attendanceRecords as $record) { switch ($record->status) { case 'present': $summary['present_days']++; break; case 'absent': $summary['absent_days']++; break; case 'half_day': $summary['half_days']++; break; case 'on_leave': $summary['leave_days']++; break; case 'holiday': $summary['holiday_days']++; break; } if ($record->overtime_hours > 0) { $summary['overtime_hours'] += $record->overtime_hours; } } // Calculate total working days (excluding holidays) $summary['total_working_days'] = $summary['present_days'] + $summary['absent_days'] + $summary['half_days'] + $summary['leave_days']; // Calculate unpaid leave days $summary['unpaid_leave_days'] = $summary['absent_days'] + ($summary['half_days'] * 0.5); return $summary; } }