Initial commit

This commit is contained in:
Porawit Dongwang
2026-09-16 23:20:08 +07:00
commit 0041668dbb
32577 changed files with 3687927 additions and 0 deletions
@@ -0,0 +1,417 @@
<!DOCTYPE html>
<html lang="th" class="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kiosk - พิมพ์สลิปเงินเดือนและใบเสียภาษี</title>
<link rel="icon" type="image/png" href="<?= BASE_URL ?>/public/favicon.png">
<link rel="shortcut icon" type="image/x-icon" href="<?= BASE_URL ?>/public/favicon.ico">
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Kanit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
sans: ['Kanit', 'sans-serif'],
}
}
}
}
</script>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<style>
.kiosk-btn {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
background: linear-gradient(to bottom, #ffffff, #f1f5f9);
border: 1px solid #cbd5e1;
border-bottom-width: 6px;
border-radius: 1rem;
font-size: 2.5rem;
font-weight: 700;
color: #334155;
box-shadow: 0 4px 6px rgba(0,0,0,0.1), inset 0 -2px 0 rgba(0,0,0,0.05);
transition: all 0.1s ease;
cursor: pointer;
user-select: none;
height: 5.5rem;
}
.kiosk-btn:active {
transform: translateY(4px);
border-bottom-width: 2px;
margin-top: 4px;
box-shadow: 0 1px 2px rgba(0,0,0,0.1), inset 0 2px 4px rgba(0,0,0,0.1);
background: #f8fafc;
}
.kiosk-btn.btn-danger {
color: #ef4444;
background: linear-gradient(to bottom, #fef2f2, #fee2e2);
border-color: #fca5a5;
border-bottom-color: #f87171;
}
.kiosk-btn.btn-danger:active {
background: #fee2e2;
}
.kiosk-btn.btn-gray {
color: #64748b;
background: linear-gradient(to bottom, #f8fafc, #f1f5f9);
border-color: #cbd5e1;
border-bottom-color: #94a3b8;
}
.kiosk-btn.btn-gray:active {
background: #f1f5f9;
}
.digit-box {
@apply w-8 h-12 md:w-11 md:h-14 flex items-center justify-center text-3xl font-bold bg-white border-2 border-slate-200 rounded-lg shadow-inner text-slate-800 transition-colors;
}
.digit-box.filled {
@apply border-sky-400 bg-sky-50 text-sky-700 shadow-sm;
}
.digit-box.active {
@apply border-sky-500 ring-2 ring-sky-200 shadow-sm scale-110 transform transition-transform;
}
.numpad-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
/* Hide scrollbar for clean kiosk look */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f5f9;
}
::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
</style>
</head>
<body class="bg-gradient-to-br from-slate-100 to-slate-200 min-h-screen flex flex-col items-center justify-center p-4 sm:p-8">
<div class="w-full max-w-6xl flex flex-col md:flex-row gap-8 bg-white/80 backdrop-blur-xl p-8 rounded-[2.5rem] shadow-2xl border border-white/50">
<!-- Left Side: Numpad -->
<div class="flex-1 flex flex-col items-center border-b md:border-b-0 md:border-r border-slate-200 pb-8 md:pb-0 md:pr-8">
<h1 class="text-3xl font-bold text-slate-800 mb-2">ตรวจสอบข้อมูลเอกสาร</h1>
<p class="text-slate-500 mb-8 text-center text-lg">กรุณาระบุเลขประจำตัวประชาชน 13 หลัก</p>
<div class="w-full max-w-lg mb-8 relative flex justify-center gap-1.5 md:gap-2 items-center" id="digit_container">
<div class="digit-box active" id="digit-0"></div>
<div class="text-xl md:text-2xl font-bold text-slate-300">-</div>
<div class="digit-box" id="digit-1"></div>
<div class="digit-box" id="digit-2"></div>
<div class="digit-box" id="digit-3"></div>
<div class="digit-box" id="digit-4"></div>
<div class="text-xl md:text-2xl font-bold text-slate-300">-</div>
<div class="digit-box" id="digit-5"></div>
<div class="digit-box" id="digit-6"></div>
<div class="digit-box" id="digit-7"></div>
<div class="digit-box" id="digit-8"></div>
<div class="digit-box" id="digit-9"></div>
<div class="text-xl md:text-2xl font-bold text-slate-300">-</div>
<div class="digit-box" id="digit-10"></div>
<div class="digit-box" id="digit-11"></div>
<div class="text-xl md:text-2xl font-bold text-slate-300">-</div>
<div class="digit-box" id="digit-12"></div>
</div>
<div class="w-full max-w-sm numpad-container">
<button type="button" class="kiosk-btn" onclick="pressKey('1')">1</button>
<button type="button" class="kiosk-btn" onclick="pressKey('2')">2</button>
<button type="button" class="kiosk-btn" onclick="pressKey('3')">3</button>
<button type="button" class="kiosk-btn" onclick="pressKey('4')">4</button>
<button type="button" class="kiosk-btn" onclick="pressKey('5')">5</button>
<button type="button" class="kiosk-btn" onclick="pressKey('6')">6</button>
<button type="button" class="kiosk-btn" onclick="pressKey('7')">7</button>
<button type="button" class="kiosk-btn" onclick="pressKey('8')">8</button>
<button type="button" class="kiosk-btn" onclick="pressKey('9')">9</button>
<button type="button" class="kiosk-btn btn-danger" onclick="clearKey()">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-8 h-8"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
</button>
<button type="button" class="kiosk-btn" onclick="pressKey('0')">0</button>
<button type="button" class="kiosk-btn btn-gray" onclick="backspaceKey()">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-8 h-8"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9.75L14.25 12m0 0l2.25 2.25M14.25 12l2.25-2.25M14.25 12L12 14.25m-2.58 4.92l-6.375-6.375a1.125 1.125 0 010-1.59L9.42 4.83c.211-.211.498-.33.796-.33H19.5a2.25 2.25 0 012.25 2.25v10.5a2.25 2.25 0 01-2.25 2.25h-9.284c-.298 0-.585-.119-.796-.33z" /></svg>
</button>
</div>
</div>
<!-- Right Side: Results -->
<div class="flex-1 flex flex-col" id="results_pane">
<div class="flex-1 flex flex-col items-center justify-center text-center opacity-50" id="waiting_state">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor" class="w-32 h-32 text-slate-300 mb-6"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z" /></svg>
<p class="text-2xl font-medium text-slate-600">รอการระบุข้อมูล</p>
<p class="text-slate-500 mt-2">พิมพ์รหัสบัตรประชาชนเพื่อค้นหาเอกสาร</p>
</div>
<div class="flex-1 hidden flex-col" id="data_state">
<div class="bg-sky-50 rounded-2xl p-6 mb-6">
<p class="text-sky-600 font-medium text-sm">ผู้ใช้งาน</p>
<h2 class="text-2xl font-bold text-slate-800 mt-1" id="person_name">ชื่อ - สกุล</h2>
</div>
<!-- Tabs -->
<div class="flex gap-2 mb-6 p-1 bg-slate-100 rounded-xl">
<button class="flex-1 py-3 text-center rounded-lg font-semibold bg-white text-sky-600 shadow-sm transition-all" id="tab_slip" onclick="switchTab('slip')">สลิปเงินเดือน</button>
<button class="flex-1 py-3 text-center rounded-lg font-semibold text-slate-500 hover:text-slate-700 transition-all" id="tab_tax" onclick="switchTab('tax')">ใบเสียภาษี 50 ทวิ</button>
</div>
<div class="flex-1 overflow-y-auto pr-2 relative flex flex-col">
<!-- Slips List -->
<div id="slips_list_container" class="flex flex-col h-full">
<div id="slips_list" class="grid grid-cols-1 sm:grid-cols-2 gap-4 flex-1">
<!-- Rendered via JS -->
</div>
<div class="mt-4 pt-4 border-t border-slate-200 hidden" id="slip_bulk_action">
<button onclick="printSelectedSlips()" class="w-full py-4 bg-sky-500 hover:bg-sky-600 text-white rounded-xl font-semibold text-lg transition-colors flex items-center justify-center gap-2 shadow-md hover:shadow-lg">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-6 h-6"><path stroke-linecap="round" stroke-linejoin="round" d="M6.72 13.829c-.24.03-.48.062-.72.096m.72-.096a42.415 42.415 0 0110.56 0m-10.56 0L6.34 18m10.94-4.171c.24.03.48.062.72.096m-.72-.096L17.66 18m0 0l.229 2.523a1.125 1.125 0 01-1.12 1.227H7.231c-.662 0-1.18-.568-1.12-1.227L6.34 18m11.318 0h1.091A2.25 2.25 0 0021 15.75V9.456c0-1.081-.768-2.015-1.837-2.175a48.055 48.055 0 00-1.913-.247M6.34 18H5.25A2.25 2.25 0 013 15.75V9.456c0-1.081.768-2.015 1.837-2.175a48.041 48.041 0 011.913-.247m10.5 0a48.536 48.536 0 00-10.5 0v2.756a2.25 2.25 0 002.25 2.25h6a2.25 2.25 0 002.25-2.25V9.229z" /></svg>
พิมพ์สลิปที่เลือก
</button>
</div>
</div>
<!-- Tax List -->
<div id="taxes_list" class="hidden grid grid-cols-1 sm:grid-cols-2 gap-4">
<!-- Rendered via JS -->
</div>
</div>
</div>
</div>
</div>
<!-- Alert Modal -->
<div id="alert_modal" class="fixed inset-0 bg-slate-900/50 backdrop-blur-sm z-50 flex items-center justify-center hidden opacity-0 transition-opacity">
<div class="bg-white rounded-3xl p-8 max-w-sm w-full mx-4 text-center transform scale-95 transition-transform duration-300" id="alert_content">
<div class="w-20 h-20 bg-red-100 text-red-500 rounded-full flex items-center justify-center mx-auto mb-6">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-10 h-10"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
</div>
<h3 class="text-2xl font-bold text-slate-800 mb-2">ไม่พบข้อมูล</h3>
<p class="text-slate-500 mb-8" id="alert_message">กรุณาตรวจสอบรหัสบัตรประชาชนอีกครั้ง</p>
<button onclick="closeAlert()" class="w-full py-4 bg-slate-800 hover:bg-slate-700 text-white rounded-xl font-semibold text-lg transition-colors">ตกลง</button>
</div>
</div>
<script>
const nidInput = document.getElementById('nid_input');
let currentNid = '';
const thaiMonths = {
1: 'มกราคม', 2: 'กุมภาพันธ์', 3: 'มีนาคม', 4: 'เมษายน',
5: 'พฤษภาคม', 6: 'มิถุนายน', 7: 'กรกฎาคม', 8: 'สิงหาคม',
9: 'กันยายน', 10: 'ตุลาคม', 11: 'พฤศจิกายน', 12: 'ธันวาคม'
};
function pressKey(num) {
if (currentNid.length < 13) {
currentNid += num;
updateDisplay();
if (currentNid.length === 13) {
searchData();
}
}
}
function backspaceKey() {
if (currentNid.length > 0) {
currentNid = currentNid.slice(0, -1);
updateDisplay();
resetUI();
}
}
function clearKey() {
currentNid = '';
updateDisplay();
resetUI();
}
function updateDisplay() {
for (let i = 0; i < 13; i++) {
let el = document.getElementById('digit-' + i);
if (i < currentNid.length) {
el.innerText = currentNid[i];
el.classList.add('filled');
el.classList.remove('active');
} else if (i === currentNid.length) {
el.innerText = '';
el.classList.remove('filled');
el.classList.add('active');
} else {
el.innerText = '';
el.classList.remove('filled');
el.classList.remove('active');
}
}
}
function resetUI() {
$('#waiting_state').removeClass('hidden');
$('#data_state').addClass('hidden');
}
function showAlert(msg) {
$('#alert_message').text(msg);
const modal = $('#alert_modal');
modal.removeClass('hidden');
setTimeout(() => {
modal.removeClass('opacity-0');
$('#alert_content').removeClass('scale-95');
}, 10);
}
function closeAlert() {
const modal = $('#alert_modal');
modal.addClass('opacity-0');
$('#alert_content').addClass('scale-95');
setTimeout(() => {
modal.addClass('hidden');
clearKey();
}, 300);
}
function switchTab(tab) {
if (tab === 'slip') {
$('#tab_slip').addClass('bg-white text-sky-600 shadow-sm').removeClass('text-slate-500 hover:text-slate-700');
$('#tab_tax').removeClass('bg-white text-sky-600 shadow-sm').addClass('text-slate-500 hover:text-slate-700');
$('#slips_list_container').removeClass('hidden');
$('#taxes_list').addClass('hidden');
} else {
$('#tab_tax').addClass('bg-white text-sky-600 shadow-sm').removeClass('text-slate-500 hover:text-slate-700');
$('#tab_slip').removeClass('bg-white text-sky-600 shadow-sm').addClass('text-slate-500 hover:text-slate-700');
$('#taxes_list').removeClass('hidden');
$('#slips_list_container').addClass('hidden');
}
}
function searchData() {
$('#waiting_state').html(`
<div class="animate-spin rounded-full h-16 w-16 border-t-4 border-b-4 border-sky-500 mb-6"></div>
<p class="text-xl font-medium text-slate-600">กำลังค้นหาข้อมูล...</p>
`);
$.post('<?= BASE_URL ?>/kiosk/search', { national_id: currentNid }, function(response) {
if (response.status === 'success') {
$('#person_name').text(response.data.name);
// Render Slips
let slipsHtml = '';
if (response.data.slips.length > 0) {
response.data.slips.forEach(slip => {
let monthName = thaiMonths[parseInt(slip.month_no)];
let yearTh = parseInt(slip.year_no) > 2500 ? parseInt(slip.year_no) : parseInt(slip.year_no) + 543;
let paddedMonth = slip.month_no.toString().padStart(2, '0');
slipsHtml += `
<label class="flex items-center justify-between p-4 bg-white border border-slate-200 rounded-2xl cursor-pointer hover:border-sky-300 hover:shadow-md transition-all group has-[:checked]:border-sky-500 has-[:checked]:bg-sky-50/50 has-[:checked]:ring-1 has-[:checked]:ring-sky-500">
<div class="flex items-center gap-4">
<div class="relative flex items-center">
<input type="checkbox" class="slip-checkbox peer h-6 w-6 cursor-pointer appearance-none rounded-md border-2 border-slate-300 checked:border-sky-500 checked:bg-sky-500 transition-all" value="${paddedMonth}-${slip.year_no}">
<svg class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-4 h-4 text-white opacity-0 peer-checked:opacity-100 pointer-events-none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>
</div>
<div>
<p class="font-semibold text-slate-800 text-lg">เดือน ${monthName}</p>
<p class="text-sm text-slate-500">พ.ศ. ${yearTh}</p>
</div>
</div>
<a href="<?= BASE_URL ?>/kiosk/print/slip/${currentNid}/${paddedMonth}/${slip.year_no}" target="_blank" onclick="event.stopPropagation();" class="w-10 h-10 rounded-full bg-slate-50 flex items-center justify-center text-slate-400 hover:bg-sky-100 hover:text-sky-600 transition-colors" title="พิมพ์เดี่ยว">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5"><path stroke-linecap="round" stroke-linejoin="round" d="M6.72 13.829c-.24.03-.48.062-.72.096m.72-.096a42.415 42.415 0 0110.56 0m-10.56 0L6.34 18m10.94-4.171c.24.03.48.062.72.096m-.72-.096L17.66 18m0 0l.229 2.523a1.125 1.125 0 01-1.12 1.227H7.231c-.662 0-1.18-.568-1.12-1.227L6.34 18m11.318 0h1.091A2.25 2.25 0 0021 15.75V9.456c0-1.081-.768-2.015-1.837-2.175a48.055 48.055 0 00-1.913-.247M6.34 18H5.25A2.25 2.25 0 013 15.75V9.456c0-1.081.768-2.015 1.837-2.175a48.041 48.041 0 011.913-.247m10.5 0a48.536 48.536 0 00-10.5 0v2.756a2.25 2.25 0 002.25 2.25h6a2.25 2.25 0 002.25-2.25V9.229z" /></svg>
</a>
</label>
`;
});
$('#slip_bulk_action').removeClass('hidden');
} else {
slipsHtml = '<p class="text-slate-500 col-span-2 text-center py-4">ไม่พบข้อมูลสลิปเงินเดือน</p>';
$('#slip_bulk_action').addClass('hidden');
}
$('#slips_list').html(slipsHtml);
// Render Taxes
let taxesHtml = '';
if (response.data.taxes.length > 0) {
response.data.taxes.forEach(tax => {
let yearTh = parseInt(tax.year_no) > 2500 ? parseInt(tax.year_no) : parseInt(tax.year_no) + 543;
taxesHtml += `
<a href="<?= BASE_URL ?>/kiosk/print/tax/${currentNid}/${tax.year_no}" target="_blank" class="flex items-center justify-between p-4 bg-white border border-slate-200 rounded-2xl hover:border-orange-300 hover:shadow-md transition-all group">
<div>
<p class="font-semibold text-slate-800 text-lg">ใบ 50 ทวิ</p>
<p class="text-sm text-slate-500">ประจำปี ${yearTh}</p>
</div>
<div class="w-10 h-10 rounded-full bg-slate-50 flex items-center justify-center text-slate-400 group-hover:bg-orange-50 group-hover:text-orange-500 transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /></svg>
</div>
</a>
`;
});
} else {
taxesHtml = '<p class="text-slate-500 col-span-2 text-center py-4">ไม่พบข้อมูลใบเสียภาษี</p>';
}
$('#taxes_list').html(taxesHtml);
switchTab('slip');
$('#waiting_state').addClass('hidden');
$('#data_state').removeClass('hidden');
} else {
showAlert(response.message || 'ไม่พบข้อมูล');
$('#waiting_state').html(`
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor" class="w-32 h-32 text-slate-300 mb-6"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z" /></svg>
<p class="text-2xl font-medium text-slate-600">รอการระบุข้อมูล</p>
<p class="text-slate-500 mt-2">พิมพ์รหัสบัตรประชาชนเพื่อค้นหาเอกสาร</p>
`);
}
}, 'json').fail(function() {
showAlert('เกิดข้อผิดพลาดในการเชื่อมต่อเซิร์ฟเวอร์');
});
}
function printSelectedSlips() {
let selected = [];
$('.slip-checkbox:checked').each(function() {
selected.push($(this).val());
});
if (selected.length === 0) {
showAlert('กรุณาเลือกอย่างน้อย 1 รายการ');
return;
}
// Create a dynamic form to submit
let form = document.createElement('form');
form.method = 'POST';
form.action = '<?= BASE_URL ?>/kiosk/print/slipBulk';
form.target = '_blank';
let inputNid = document.createElement('input');
inputNid.type = 'hidden';
inputNid.name = 'national_id';
inputNid.value = currentNid;
form.appendChild(inputNid);
selected.forEach(val => {
let inputVal = document.createElement('input');
inputVal.type = 'hidden';
inputVal.name = 'periods[]';
inputVal.value = val;
form.appendChild(inputVal);
});
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
}
// Initialize display on load
updateDisplay();
</script>
</body>
</html>
@@ -0,0 +1,309 @@
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>สลิปเงินเดือน - Kiosk</title>
<!-- Google Fonts: Sarabun (Standard Thai Font) -->
<link href="https://fonts.googleapis.com/css2?family=Sarabun:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
sans: ['Sarabun', 'sans-serif'],
}
}
}
}
</script>
<style>
@media print {
body { background: white; margin: 0; padding: 0; }
.no-print { display: none !important; }
@page { size: A4; margin: 0; } /* Set A4 without margins */
* { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }
.slip-container {
padding: 1cm !important;
box-shadow: none !important;
max-width: 100% !important;
margin: 0 !important;
width: 210mm !important;
height: 297mm !important;
box-sizing: border-box !important;
overflow: hidden !important;
border: none !important;
}
.page-break { page-break-after: always; }
}
body { font-size: <?= htmlspecialchars($slipFontSize ?? '11pt') ?>; color: #1e293b; background-color: #f8fafc; font-family: 'Sarabun', sans-serif; }
.slip-container {
width: 210mm;
min-height: 297mm;
box-sizing: border-box;
margin: 2cm auto;
background: white;
padding: 1cm;
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
border-radius: 0.5rem;
border: 1px solid #e2e8f0;
}
table { width: 100%; border-collapse: collapse; margin-bottom: 0.75rem; }
th, td { border: 1px solid #cbd5e1; padding: 0.2rem 0.5rem; }
.bg-gray-header { background-color: #f1f5f9 !important; }
.bg-gray-light { background-color: #f8fafc !important; }
.bg-gray-dark { background-color: #e2e8f0 !important; }
.bg-white { background-color: #ffffff !important; }
.border-none { border: none !important; }
.border-l { border-left: 1px solid #cbd5e1 !important; }
.border-r { border-right: 1px solid #cbd5e1 !important; }
.border-t { border-top: 1px solid #cbd5e1 !important; }
.border-b { border-bottom: 1px solid #cbd5e1 !important; }
.text-center { text-align: center; }
.text-right { text-align: right; }
.font-bold { font-weight: 700; }
.text-sm { font-size: 0.875rem; }
.w-col-name { width: 35%; }
.w-col-amt { width: 15%; }
.table-rounded { border-radius: 0.5rem; overflow: hidden; border: 1px solid #cbd5e1; }
.table-rounded table { margin-bottom: 0; }
.table-rounded th, .table-rounded td { border-color: #e2e8f0; }
.table-rounded tr:last-child td { border-bottom: none; }
</style>
</head>
<body class="bg-gray-100 flex flex-col items-center pb-10">
<!-- Print Button (Hidden when printing) -->
<div class="mt-6 mb-2 print:hidden w-full max-w-[210mm] flex justify-end gap-4">
<button onclick="window.print()" class="flex items-center gap-2 bg-sky-500 hover:bg-sky-600 text-white px-6 py-3 rounded-xl font-semibold shadow-md transition-all active:scale-95">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5"><path stroke-linecap="round" stroke-linejoin="round" d="M6.72 13.829c-.24.03-.48.062-.72.096m.72-.096a42.415 42.415 0 0110.56 0m-10.56 0L6.34 18m10.94-4.171c.24.03.48.062.72.096m-.72-.096L17.66 18m0 0l.229 2.523a1.125 1.125 0 01-1.12 1.227H7.231c-.662 0-1.18-.568-1.12-1.227L6.34 18m11.318 0h1.091A2.25 2.25 0 0021 15.75V9.456c0-1.081-.768-2.015-1.837-2.175a48.055 48.055 0 00-1.913-.247M6.34 18H5.25A2.25 2.25 0 013 15.75V9.456c0-1.081.768-2.015 1.837-2.175a48.041 48.041 0 011.913-.247m10.5 0a48.536 48.536 0 00-10.5 0v2.756a2.25 2.25 0 002.25 2.25h6a2.25 2.25 0 002.25-2.25V9.229z" /></svg>
พิมพ์สลิปที่เลือก
</button>
<button onclick="window.close()" class="flex items-center gap-2 bg-slate-200 hover:bg-slate-300 text-slate-700 px-6 py-3 rounded-xl font-semibold shadow-sm transition-all active:scale-95">
ปิดหน้าต่าง
</button>
</div>
<?php
$totalSlips = count($bulkData);
foreach ($bulkData as $index => $data):
$salary = $data['salary'];
$hrData = $data['hrData'] ?? [];
$bankProfile = $data['bankProfile'] ?? [];
$incomes = $data['incomes'];
$deductions = $data['deductions'];
$month = $data['month'];
$year = $data['year'];
$thaiMonths = [
'01' => 'มกราคม', '02' => 'กุมภาพันธ์', '03' => 'มีนาคม', '04' => 'เมษายน',
'05' => 'พฤษภาคม', '06' => 'มิถุนายน', '07' => 'กรกฎาคม', '08' => 'สิงหาคม',
'09' => 'กันยายน', '10' => 'ตุลาคม', '11' => 'พฤศจิกายน', '12' => 'ธันวาคม'
];
$thaiYear = $year + 543;
$monthName = $thaiMonths[$month] ?? '';
// Grouping by Fund Source
$cgdIncomes = [];
$hospIncomes = [];
foreach($incomes as $inc) {
if ($inc['amount'] > 0) {
if (($inc['fund_source'] ?? '') === 'CGD') $cgdIncomes[] = $inc;
else $hospIncomes[] = $inc;
}
}
$cgdDeductions = [];
$hospDeductions = [];
foreach($deductions as $ded) {
if ($ded['amount'] > 0) {
if (($ded['fund_source'] ?? '') === 'CGD') $cgdDeductions[] = $ded;
else $hospDeductions[] = $ded;
}
}
$cgdTotalIncome = array_sum(array_column($cgdIncomes, 'amount'));
$cgdTotalDeduction = array_sum(array_column($cgdDeductions, 'amount'));
$hospTotalIncome = array_sum(array_column($hospIncomes, 'amount'));
$hospTotalDeduction = array_sum(array_column($hospDeductions, 'amount'));
$netSalary = ($cgdTotalIncome + $hospTotalIncome) - ($cgdTotalDeduction + $hospTotalDeduction);
$pageBreakClass = ($index < $totalSlips - 1) ? 'page-break' : '';
?>
<div class="slip-container <?= $pageBreakClass ?>" style="position: relative;">
<!-- Header Info Table -->
<table>
<tr>
<td colspan="4" class="bg-gray-header p-6 text-center relative border-b-0 rounded-t-lg">
<img src="<?= BASE_URL ?>/public/img/logo.png" alt="Logo" class="h-20 absolute top-4 left-1/2 transform -translate-x-1/2" onerror="this.style.display='none'">
<h1 class="text-3xl font-bold mt-20 text-slate-800">โรงพยาบาลเกาะสมุย</h1>
</td>
</tr>
<tr>
<td class="bg-gray-light font-bold w-[20%]">ชื่อ - สกุล</td>
<td class="w-[30%]"><?= htmlspecialchars($hrData['HR_FNAME'] ?? $salary['first_name']) ?> <?= htmlspecialchars($hrData['HR_LNAME'] ?? $salary['last_name']) ?></td>
<td class="bg-gray-light font-bold w-[20%]">ตำแหน่ง</td>
<td class="w-[30%]"><?= htmlspecialchars($hrData['HR_POSITION_NAME'] ?? '-') ?></td>
</tr>
<tr>
<td class="bg-gray-light font-bold">กรม</td>
<td>สำนักงานปลัดกระทรวงสาธารณสุข</td>
<td class="bg-gray-light font-bold">ระดับ</td>
<td><?= htmlspecialchars($hrData['HR_LEVEL_NAME'] ?? 'ชำนาญการ') ?></td>
</tr>
<tr>
<td class="bg-gray-light font-bold">สำนัก/กอง/ศูนย์</td>
<td>โรงพยาบาลทั่วไปเกาะสมุย</td>
<td class="bg-gray-light font-bold">เลขที่ตำแหน่ง</td>
<td><?= htmlspecialchars($hrData['HR_POSITION_NUM'] ?? '-') ?></td>
</tr>
<tr>
<td class="bg-gray-light font-bold">รอบจ่าย</td>
<td>เดือน <?= $monthName ?> ปี <?= $thaiYear ?></td>
<td class="bg-gray-light font-bold">แผนก</td>
<td><?= htmlspecialchars($hrData['HR_DEPARTMENT_SUB_SUB_NAME'] ?? '-') ?></td>
</tr>
<tr>
<td class="bg-gray-light font-bold">โอนเงินเข้า</td>
<td class="w-[30%]"><?= htmlspecialchars($bankProfile['bank_name'] ?? 'ธนาคารกรุงไทย จำกัด(มหาชน)') ?></td>
<td class="bg-gray-light font-bold w-[20%]">เลขที่บัญชี</td>
<td class="w-[30%]"><?= htmlspecialchars($bankProfile['bank_account'] ?? '-') ?></td>
</tr>
</table>
<?php if (!empty($cgdIncomes) || !empty($cgdDeductions)): ?>
<!-- Section 1: กรมบัญชีกลาง -->
<table class="mb-4">
<tr>
<td colspan="4" class="bg-gray-dark font-bold text-lg p-3 text-slate-800 border-x-0 border-t-0">รายการจากกรมบัญชีกลาง</td>
</tr>
<tr class="bg-gray-light text-center font-bold">
<td class="w-col-name">รายรับ</td>
<td class="w-col-amt">จำนวน (บาท)</td>
<td class="w-col-name">รายจ่าย</td>
<td class="w-col-amt">จำนวน (บาท)</td>
</tr>
<?php
$maxCgdRows = max(count($cgdIncomes), count($cgdDeductions), 1);
for($i=0; $i<$maxCgdRows; $i++):
$inc = $cgdIncomes[$i] ?? null;
$ded = $cgdDeductions[$i] ?? null;
?>
<tr>
<td class="border-t-0 border-b-0 border-l border-r"><?= $inc ? htmlspecialchars($inc['name']) : '&nbsp;' ?></td>
<td class="border-t-0 border-b-0 border-l border-r text-right"><?= $inc ? number_format($inc['amount'], 2) : '' ?></td>
<td class="border-t-0 border-b-0 border-l border-r"><?= $ded ? htmlspecialchars($ded['name']) : '' ?></td>
<td class="border-t-0 border-b-0 border-l border-r text-right"><?= $ded ? number_format($ded['amount'], 2) : '' ?></td>
</tr>
<?php endfor; ?>
<tr>
<td class="border-t border-b border-l border-r font-bold text-center bg-gray-light text-slate-700">รวมรับ</td>
<td class="border-t border-b border-l border-r text-right font-medium"><?= number_format($cgdTotalIncome, 2) ?></td>
<td class="border-t border-b border-l border-r font-bold text-center bg-gray-light text-slate-700">รวมจ่าย</td>
<td class="border-t border-b border-l border-r text-right font-medium"><?= number_format($cgdTotalDeduction, 2) ?></td>
</tr>
<tr>
<td colspan="2" class="border-t border-b border-l border-r bg-white" style="border-right: none;"></td>
<td class="border-t border-b border-l border-r font-bold text-center bg-gray-dark text-slate-800">รับสุทธิจากกรมบัญชีกลาง</td>
<td class="border-t border-b border-l border-r text-right bg-white font-bold text-sky-700"><?= number_format($cgdTotalIncome - $cgdTotalDeduction, 2) ?></td>
</tr>
</table>
<?php endif; ?>
<?php if (!empty($hospIncomes) || !empty($hospDeductions)): ?>
<!-- Section 2: โรงพยาบาล -->
<table class="mb-4">
<tr>
<td colspan="4" class="bg-gray-dark font-bold text-lg p-3 text-slate-800 border-x-0 border-t-0">รายการจากโรงพยาบาล</td>
</tr>
<tr class="bg-gray-light text-center font-bold">
<td class="w-col-name">รายรับ</td>
<td class="w-col-amt">จำนวน (บาท)</td>
<td class="w-col-name">รายจ่าย</td>
<td class="w-col-amt">จำนวน (บาท)</td>
</tr>
<?php
$maxHospRows = max(count($hospIncomes), count($hospDeductions), 1);
for($i=0; $i<$maxHospRows; $i++):
$inc = $hospIncomes[$i] ?? null;
$ded = $hospDeductions[$i] ?? null;
?>
<tr>
<td class="border-t-0 border-b-0 border-l border-r"><?= $inc ? htmlspecialchars($inc['name']) : '&nbsp;' ?></td>
<td class="border-t-0 border-b-0 border-l border-r text-right"><?= $inc ? number_format($inc['amount'], 2) : '' ?></td>
<td class="border-t-0 border-b-0 border-l border-r"><?= $ded ? htmlspecialchars($ded['name']) : '' ?></td>
<td class="border-t-0 border-b-0 border-l border-r text-right"><?= $ded ? number_format($ded['amount'], 2) : '' ?></td>
</tr>
<?php endfor; ?>
<tr>
<td class="border-t border-b border-l border-r font-bold text-center bg-gray-light text-slate-700">รวมรับ</td>
<td class="border-t border-b border-l border-r text-right font-medium"><?= number_format($hospTotalIncome, 2) ?></td>
<td class="border-t border-b border-l border-r font-bold text-center bg-gray-light text-slate-700">รวมจ่าย</td>
<td class="border-t border-b border-l border-r text-right font-medium"><?= number_format($hospTotalDeduction, 2) ?></td>
</tr>
<tr>
<td colspan="2" class="border-t border-b border-l border-r bg-white" style="border-right: none;"></td>
<td class="border-t border-b border-l border-r font-bold text-center bg-gray-dark text-slate-800">รับสุทธิจากโรงพยาบาล</td>
<td class="border-t border-b border-l border-r text-right bg-white font-bold text-sky-700"><?= number_format($hospTotalIncome - $hospTotalDeduction, 2) ?></td>
</tr>
</table>
<?php endif; ?>
<!-- Grand Totals -->
<table class="mb-4">
<tr>
<td class="bg-gray-dark font-bold text-center text-xl p-4 w-[85%] text-slate-800 rounded-l-lg border-r-0">รวมรับสุทธิทั้งสิ้น</td>
<td class="text-right font-bold text-xl p-4 w-[15%] bg-white text-emerald-600 rounded-r-lg border-l-0"><?= number_format($netSalary, 2) ?></td>
</tr>
</table>
<!-- Signature -->
<div class="mt-16 flex justify-end pr-8">
<table class="text-center w-72 border-none">
<tr class="border-none">
<td class="text-right w-16 pr-2 border-none pb-1 align-bottom text-slate-700">ลงชื่อ</td>
<td class="border-b border-dotted border-slate-300 border-t-0 border-l-0 border-r-0 h-16 align-bottom pb-1 relative">
<?php
$sigPath = APP_ROOT . '/public/img/signature.png';
if (file_exists($sigPath)):
$modTime = filemtime($sigPath);
// signatureScale from Controller (default 100)
$scale = $signatureScale ?? 100;
$maxHeight = 3 * ($scale / 100) . 'rem'; // 3rem is roughly 48px (h-12)
// Signature position shift (default 0)
$posX = $signaturePosX ?? '0';
$posY = $signaturePosY ?? '0';
?>
<img src="<?= BASE_URL ?>/public/img/signature.png?t=<?= $modTime ?>" alt="Signature" class="mx-auto object-contain mix-blend-multiply absolute bottom-1 left-0 right-0" style="max-height: <?= $maxHeight ?>; transform: translate(<?= $posX ?>px, <?= $posY ?>px);" onerror="this.style.display='none'">
<?php endif; ?>
</td>
</tr>
<tr class="border-none">
<td class="border-none"></td>
<td class="pt-4 border-none"><?= htmlspecialchars($signatureName ?? '( นางณัฐฐิณี เรืองทอง )') ?></td>
</tr>
<tr class="border-none">
<td class="border-none"></td>
<td class="border-none"><?= htmlspecialchars($signaturePosition ?? 'นักวิชาการเงินและบัญชี') ?></td>
</tr>
</table>
</div>
</div>
<?php endforeach; ?>
</body>
</html>