465 lines
19 KiB
PHP
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;
|
|
}
|
|
}
|
|
?>
|