Files
2026-09-16 23:20:08 +07:00

465 lines
19 KiB
PHP

<?php
namespace app\Controllers;
use app\Models\PayrollModel;
use app\Models\EmployeeProfileModel;
class PayrollController extends Controller {
public function __construct() {
if (empty($_SESSION['user_id'])) {
$_SESSION['error'] = 'กรุณาเข้าสู่ระบบก่อนใช้งาน';
header('Location: ' . BASE_URL . '/login');
exit;
}
}
public function index() {
$payrollModel = new PayrollModel();
$periods = $payrollModel->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;
}
}
?>