getAllPayrollPeriods(); $this->view('payroll/index', [ 'title' => 'จัดการงวดเงินเดือน | ' . APP_NAME, 'activeMenu' => 'payroll', 'periods' => $periods ]); } public function store() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { header('Location: ' . BASE_URL . '/payroll'); exit; } $yearNo = isset($_POST['year_no']) ? (int)$_POST['year_no'] : 0; $monthNo = isset($_POST['month_no']) ? (int)$_POST['month_no'] : 0; if ($yearNo < 2000 || $monthNo < 1 || $monthNo > 12) { $_SESSION['error'] = 'ข้อมูลปีหรือเดือนไม่ถูกต้อง'; header('Location: ' . BASE_URL . '/payroll'); exit; } $payrollModel = new PayrollModel(); if ($payrollModel->checkPeriodExists($yearNo, $monthNo)) { $_SESSION['error'] = 'งวดเงินเดือนนี้มีอยู่ในระบบแล้ว'; header('Location: ' . BASE_URL . '/payroll'); exit; } if ($payrollModel->createPayrollPeriod($yearNo, $monthNo)) { $_SESSION['success'] = "สร้างงวดเงินเดือน $monthNo/$yearNo สำเร็จ (สถานะ: Draft)"; } else { $_SESSION['error'] = 'เกิดข้อผิดพลาดในการสร้างงวดเงินเดือน'; } header('Location: ' . BASE_URL . '/payroll'); exit; } public function updateStatus() { if ($_SERVER['REQUEST_METHOD'] === 'POST') { $id = $_POST['id'] ?? null; $status = $_POST['status'] ?? null; if ($id && $status) { $payrollModel = new PayrollModel(); $success = $payrollModel->updateSalaryMonthStatus($id, $status); if ($success) { $_SESSION['success'] = "อัปเดตสถานะเป็น $status สำเร็จ ข้อมูลจะแสดงที่ตู้ Kiosk แล้ว"; } else { $_SESSION['error'] = 'เกิดข้อผิดพลาดในการอัปเดตสถานะ'; } } else { $_SESSION['error'] = 'ข้อมูลไม่ครบถ้วน'; } header('Location: ' . BASE_URL . '/payroll/details/' . $id); exit; } } public function copyPreviousMonth() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { header('Location: ' . BASE_URL . '/payroll'); exit; } $currentMonthId = isset($_POST['salary_month_id']) ? (int)$_POST['salary_month_id'] : 0; if (!$currentMonthId) { $_SESSION['error'] = 'ข้อมูลไม่ถูกต้อง'; header('Location: ' . BASE_URL . '/payroll'); exit; } $payrollModel = new PayrollModel(); $currentPeriod = $payrollModel->getSalaryMonthById($currentMonthId); if (!$currentPeriod) { $_SESSION['error'] = 'ไม่พบงวดเงินเดือนที่ระบุ'; header('Location: ' . BASE_URL . '/payroll'); exit; } if ($currentPeriod['status'] !== 'Draft') { $_SESSION['error'] = 'ไม่สามารถเพิ่มข้อมูลได้เนื่องจากงวดเงินเดือนถูกล็อคแล้ว'; header('Location: ' . BASE_URL . '/payroll/details?id=' . $currentMonthId); exit; } // Find previous month $prevMonthId = $payrollModel->getPreviousSalaryMonth($currentPeriod['year_no'], $currentPeriod['month_no']); if (!$prevMonthId) { $_SESSION['error'] = 'ไม่พบข้อมูลงวดเงินเดือนของเดือนก่อนหน้าในระบบ'; header('Location: ' . BASE_URL . '/payroll/details?id=' . $currentMonthId); exit; } $result = $payrollModel->copyFromPreviousMonth($currentMonthId, $prevMonthId); if ($result !== false) { $copiedCount = $result['copied']; $mergedCount = $result['merged']; if ($copiedCount > 0 || $mergedCount > 0) { $_SESSION['success'] = "คัดลอกข้อมูลสำเร็จ: เพิ่มพนักงานใหม่ $copiedCount คน, อัปเดตรายการรับจ่ายของโรงพยาบาล $mergedCount คน"; } else { $_SESSION['success'] = 'ไม่มีข้อมูลที่ต้องคัดลอกเพิ่มเติม (ข้อมูลถูกคัดลอกมาหมดแล้ว)'; } } else { $_SESSION['error'] = 'เกิดข้อผิดพลาดในการคัดลอกข้อมูล'; } header('Location: ' . BASE_URL . '/payroll/details?id=' . $currentMonthId); exit; } public function clearData() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { header('Location: ' . BASE_URL . '/payroll'); exit; } $salaryMonthId = isset($_POST['salary_month_id']) ? (int)$_POST['salary_month_id'] : 0; if ($salaryMonthId <= 0) { $_SESSION['error'] = 'ไม่พบข้อมูลงวดเงินเดือนที่ต้องการล้าง'; header('Location: ' . BASE_URL . '/payroll'); exit; } $payrollModel = new PayrollModel(); $period = $payrollModel->getSalaryMonthById($salaryMonthId); if (!$period) { $_SESSION['error'] = 'ไม่พบข้อมูลงวดเงินเดือนในระบบ'; header('Location: ' . BASE_URL . '/payroll'); exit; } if ($period['status'] === 'Locked' || $period['status'] === 'Published') { $_SESSION['error'] = 'ไม่สามารถล้างข้อมูลได้เนื่องจากงวดเงินเดือนถูกล็อกหรือประกาศใช้แล้ว'; header('Location: ' . BASE_URL . '/payroll'); exit; } if ($payrollModel->clearDataForPeriod($salaryMonthId)) { $_SESSION['success'] = 'ล้างข้อมูลการนำเข้าสำหรับงวดเงินเดือนนี้เรียบร้อยแล้ว'; } else { $_SESSION['error'] = 'เกิดข้อผิดพลาดในการล้างข้อมูล'; } header('Location: ' . BASE_URL . '/payroll'); exit; } public function details() { $id = isset($_GET['id']) ? (int)$_GET['id'] : 0; if ($id <= 0) { $_SESSION['error'] = 'ไม่พบข้อมูลงวดเงินเดือน'; header('Location: ' . BASE_URL . '/payroll'); exit; } $payrollModel = new PayrollModel(); $period = $payrollModel->getSalaryMonthById($id); if (!$period) { $_SESSION['error'] = 'ไม่พบข้อมูลงวดเงินเดือนในระบบ'; header('Location: ' . BASE_URL . '/payroll'); exit; } $employees = $payrollModel->getEmployeesBySalaryMonthId($id); $stats = $payrollModel->getPeriodStats($id); // Map dynamic HR data $cids = array_column($employees, 'national_id'); $hrModel = new \app\Models\HrPersonModel(); $hrPersons = $hrModel->getPersonsByNationalIds($cids); foreach ($employees as &$emp) { $cid = $emp['national_id']; if (isset($hrPersons[$cid])) { $hr = $hrPersons[$cid]; $prefix = trim($hr['HR_PREFIX_NAME'] ?? ''); $firstName = trim($hr['HR_FNAME'] ?? ''); if (!empty($prefix) && strpos($firstName, $prefix) === false) { $firstName = $prefix . $firstName; } $emp['first_name'] = $firstName; $emp['last_name'] = trim($hr['HR_LNAME'] ?? ''); $emp['position'] = trim(($hr['HR_POSITION_NAME'] ?? '') . ' ' . ($hr['HR_LEVEL_NAME'] ?? '')); $emp['department'] = trim($hr['HR_DEPARTMENT_SUB_SUB_NAME'] ?? ''); } } unset($emp); $this->view('payroll/details', [ 'title' => 'รายละเอียดงวดเงินเดือน | ' . APP_NAME, 'activeMenu' => 'payroll', 'period' => $period, 'employees' => $employees, 'stats' => $stats ]); } public function addEmployee() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { header('Location: ' . BASE_URL . '/payroll'); exit; } $salaryMonthId = isset($_POST['salary_month_id']) ? (int)$_POST['salary_month_id'] : 0; $nationalId = trim($_POST['national_id'] ?? ''); if ($salaryMonthId <= 0 || empty($nationalId)) { $_SESSION['error'] = 'ข้อมูลไม่ครบถ้วน กรุณากรอกเลขบัตรประชาชน'; header('Location: ' . BASE_URL . '/payroll/details?id=' . $salaryMonthId); exit; } // 1. Fetch from HR DB $hrModel = new \app\Models\HrPersonModel(); $hrData = $hrModel->getPersonByNationalId($nationalId); if (!$hrData) { $_SESSION['error'] = 'ไม่พบข้อมูลบุคคลนี้ในฐานข้อมูล HR (hosoffice_2566)'; header('Location: ' . BASE_URL . '/payroll/details?id=' . $salaryMonthId); exit; } // 2. Prepare Data $prefix = trim($hrData['HR_PREFIX_NAME'] ?? ''); $firstName = trim($hrData['HR_FNAME'] ?? ''); $lastName = trim($hrData['HR_LNAME'] ?? ''); if (!empty($prefix) && strpos($firstName, $prefix) === false) { $firstName = $prefix . $firstName; } $position = trim(($hrData['HR_POSITION_NAME'] ?? '') . ' ' . ($hrData['HR_LEVEL_NAME'] ?? '')); $department = trim($hrData['HR_DEPARTMENT_SUB_SUB_NAME'] ?? ''); $data = [ 'salary_month_id' => $salaryMonthId, 'employee_code' => $nationalId, 'national_id' => $nationalId, 'first_name' => $firstName, 'last_name' => $lastName, 'position' => $position, 'department' => $department, 'base_salary' => 0, 'total_income' => 0, 'total_deduction' => 0, 'net_salary' => 0 ]; // 3. Save $payrollModel = new PayrollModel(); $payrollModel->insertEmployeeSalary($data); $_SESSION['success'] = 'เพิ่มรายชื่อพนักงานสำเร็จ: ' . $firstName . ' ' . $lastName; header('Location: ' . BASE_URL . '/payroll/details?id=' . $salaryMonthId); exit; } public function deleteEmployee() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { header('Location: ' . BASE_URL . '/payroll'); exit; } $id = isset($_POST['id']) ? (int)$_POST['id'] : 0; $salaryMonthId = isset($_POST['salary_month_id']) ? (int)$_POST['salary_month_id'] : 0; if ($id <= 0 || $salaryMonthId <= 0) { $_SESSION['error'] = 'ข้อมูลไม่ถูกต้อง'; header('Location: ' . BASE_URL . '/payroll/details?id=' . $salaryMonthId); exit; } $payrollModel = new PayrollModel(); if ($payrollModel->deleteEmployeeSalary($id)) { $_SESSION['success'] = 'ลบข้อมูลพนักงานออกจากงวดนี้สำเร็จ'; } else { $_SESSION['error'] = 'เกิดข้อผิดพลาดในการลบข้อมูล'; } header('Location: ' . BASE_URL . '/payroll/details?id=' . $salaryMonthId); exit; } public function editEmployee() { $id = isset($_GET['id']) ? (int)$_GET['id'] : 0; if ($id <= 0) { $_SESSION['error'] = 'ไม่พบข้อมูลพนักงาน'; header('Location: ' . BASE_URL . '/payroll'); exit; } $payrollModel = new PayrollModel(); $employee = $payrollModel->getEmployeeSalaryById($id); if (!$employee) { $_SESSION['error'] = 'ไม่พบข้อมูลพนักงานในระบบ'; header('Location: ' . BASE_URL . '/payroll'); exit; } $period = $payrollModel->getSalaryMonthById($employee['salary_month_id']); if ($period['status'] === 'Locked' || $period['status'] === 'Published') { $_SESSION['error'] = 'งวดเงินเดือนถูกล็อกแล้ว ไม่สามารถแก้ไขข้อมูลได้'; header('Location: ' . BASE_URL . '/payroll/details?id=' . $employee['salary_month_id']); exit; } $incomes = $payrollModel->getEmployeeIncomes($id); $deductions = $payrollModel->getEmployeeDeductions($id); // Fetch HR data dynamically $hrModel = new \app\Models\HrPersonModel(); $hrData = $hrModel->getPersonByNationalId($employee['national_id']); if ($hrData) { $prefix = trim($hrData['HR_PREFIX_NAME'] ?? ''); $firstName = trim($hrData['HR_FNAME'] ?? ''); if (!empty($prefix) && strpos($firstName, $prefix) === false) { $firstName = $prefix . $firstName; } $employee['first_name'] = $firstName; $employee['last_name'] = trim($hrData['HR_LNAME'] ?? ''); $employee['position'] = trim(($hrData['HR_POSITION_NAME'] ?? '') . ' ' . ($hrData['HR_LEVEL_NAME'] ?? '')); $employee['department'] = trim($hrData['HR_DEPARTMENT_SUB_SUB_NAME'] ?? ''); } // Fetch master data for dropdowns $incomeModel = new \app\Models\IncomeMasterModel(); $masterIncomes = $incomeModel->getActiveIncomes(); $deductionModel = new \app\Models\DeductionMasterModel(); $masterDeductions = $deductionModel->getActiveDeductions(); // Fetch bank profile $profileModel = new EmployeeProfileModel(); $profile = $profileModel->getProfile($employee['national_id']); if ($profile) { $employee['bank_name'] = $profile['bank_name']; $employee['bank_account'] = $profile['bank_account']; } else { $employee['bank_name'] = 'ธนาคารกรุงไทย จำกัด(มหาชน)'; $employee['bank_account'] = ''; } $this->view('payroll/employee_edit', [ 'title' => 'แก้ไขข้อมูลเงินเดือน | ' . APP_NAME, 'activeMenu' => 'payroll', 'employee' => $employee, 'period' => $period, 'incomes' => $incomes, 'deductions' => $deductions, 'masterIncomes' => $masterIncomes, 'masterDeductions' => $masterDeductions ]); } public function updateEmployee() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { header('Location: ' . BASE_URL . '/payroll'); exit; } $id = isset($_POST['employee_salary_id']) ? (int)$_POST['employee_salary_id'] : 0; $salaryMonthId = isset($_POST['salary_month_id']) ? (int)$_POST['salary_month_id'] : 0; if ($id <= 0 || $salaryMonthId <= 0) { $_SESSION['error'] = 'ข้อมูลไม่ถูกต้อง'; header('Location: ' . BASE_URL . '/payroll'); exit; } $payrollModel = new PayrollModel(); $period = $payrollModel->getSalaryMonthById($salaryMonthId); if ($period['status'] === 'Locked' || $period['status'] === 'Published') { $_SESSION['error'] = 'งวดเงินเดือนถูกล็อกแล้ว ไม่สามารถแก้ไขข้อมูลได้'; header('Location: ' . BASE_URL . '/payroll/details?id=' . $salaryMonthId); exit; } $totalIncome = 0.0; $incomesData = []; if (isset($_POST['income_id']) && is_array($_POST['income_id'])) { for ($i = 0; $i < count($_POST['income_id']); $i++) { $incId = (int)$_POST['income_id'][$i]; $amt = (float)str_replace(',', '', $_POST['income_amount'][$i]); if ($incId > 0 && $amt > 0) { $incomesData[] = ['id' => $incId, 'amount' => $amt]; $totalIncome += $amt; } } } $totalDeduction = 0.0; $deductionsData = []; if (isset($_POST['deduction_id']) && is_array($_POST['deduction_id'])) { for ($i = 0; $i < count($_POST['deduction_id']); $i++) { $dedId = (int)$_POST['deduction_id'][$i]; $amt = (float)str_replace(',', '', $_POST['deduction_amount'][$i]); if ($dedId > 0 && $amt > 0) { $deductionsData[] = ['id' => $dedId, 'amount' => $amt]; $totalDeduction += $amt; } } } $netSalary = $totalIncome - $totalDeduction; // Save Profile (Bank info) $bankName = trim($_POST['bank_name'] ?? ''); $bankAccount = trim($_POST['bank_account'] ?? ''); $empRecord = $payrollModel->getEmployeeSalaryById($id); if ($empRecord) { $profileModel = new EmployeeProfileModel(); $profileModel->saveProfile($empRecord['national_id'], $bankName, $bankAccount); } if ($payrollModel->updateEmployeeSalaryDetails($id, 0, $incomesData, $deductionsData, $totalIncome, $totalDeduction, $netSalary)) { $_SESSION['success'] = 'อัปเดตข้อมูลเงินเดือนเรียบร้อยแล้ว'; } else { $_SESSION['error'] = 'เกิดข้อผิดพลาดในการบันทึกข้อมูล'; } header('Location: ' . BASE_URL . '/payroll/details?id=' . $salaryMonthId); exit; } } ?>