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

413 lines
22 KiB
PHP

<?php
// items.php
require_once 'config.php';
include 'header.php';
// ดึงตัวเลือกสำหรับ Filter
$stmtCategories = $pdo->prepare("SELECT DISTINCT category_code, category FROM procurement_items WHERE category IS NOT NULL AND budget_year = ? ORDER BY category_code, category");
$stmtCategories->execute([$active_year]);
$categories = $stmtCategories->fetchAll();
$stmtDepartments = $pdo->prepare("SELECT DISTINCT department FROM procurement_items WHERE department IS NOT NULL AND budget_year = ? ORDER BY department");
$stmtDepartments->execute([$active_year]);
$departments = $stmtDepartments->fetchAll(PDO::FETCH_COLUMN);
$stmtStatuses = $pdo->prepare("SELECT DISTINCT status FROM procurement_items WHERE status IS NOT NULL AND budget_year = ? ORDER BY status");
$stmtStatuses->execute([$active_year]);
$statuses = $stmtStatuses->fetchAll(PDO::FETCH_COLUMN);
// ดึงรายการจัดซื้อทั้งหมด
$stmt = $pdo->prepare("SELECT * FROM procurement_items WHERE budget_year = ? ORDER BY id ASC");
$stmt->execute([$active_year]);
$items = $stmt->fetchAll();
?>
<style>
/* ตั้งค่าบังคับจัดกึ่งกลางให้ชื่อหัวคอลัมน์ทั้งหมด (th) */
#procurementTable th {
text-align: center !important;
vertical-align: middle !important;
}
/* Custom Progress Bar Style */
.custom-progress-container {
background-color: #e9ecef;
border-radius: 20px;
height: 12px;
overflow: hidden;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
}
.progress-bar-danger {
background: linear-gradient(90deg, #ff758c 0%, #ff7eb3 100%);
}
.progress-bar-info {
background: linear-gradient(90deg, #36d1dc 0%, #5b86e5 100%);
}
.progress-bar-success-custom {
background: linear-gradient(90deg, #11998e 0%, #38ef7d 100%);
}
.badge-progress {
font-size: 0.75rem;
padding: 3px 8px;
border-radius: 12px;
min-width: 42px;
text-align: center;
}
/* Custom DataTables Sort Icons */
table.dataTable thead>tr>th.sorting:before,
table.dataTable thead>tr>th.sorting:after,
table.dataTable thead>tr>th.sorting_asc:before,
table.dataTable thead>tr>th.sorting_asc:after,
table.dataTable thead>tr>th.sorting_desc:before,
table.dataTable thead>tr>th.sorting_desc:after,
table.dataTable thead>tr>td.sorting:before,
table.dataTable thead>tr>td.sorting:after,
table.dataTable thead>tr>td.sorting_asc:before,
table.dataTable thead>tr>td.sorting_asc:after,
table.dataTable thead>tr>td.sorting_desc:before,
table.dataTable thead>tr>td.sorting_desc:after {
display: none !important;
}
table.dataTable thead th.sorting {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="%23adb5bd" class="bi bi-arrow-down-up" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M11.5 15a.5.5 0 0 0 .5-.5V2.707l3.146 3.147a.5.5 0 0 0 .708-.708l-4-4a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L11 2.707V14.5a.5.5 0 0 0 .5.5zm-7-14a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L4 13.293V1.5a.5.5 0 0 1 .5-.5z"/></svg>') !important;
background-position: right 6px center !important;
background-repeat: no-repeat !important;
padding-right: 25px !important;
}
table.dataTable thead th.sorting_asc {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="%230d6efd" class="bi bi-sort-alpha-down" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M10.082 5.629 9.664 7H8.598l1.789-5.332h1.234L13.402 7h-1.12l-.419-1.371h-1.781zm1.57-.785L11 2.687h-.047l-.652 2.157h1.351z"/><path d="M12.96 14H8.5v-1.299l3.46-3.94h-3.46V7.49h4.46v1.298l-3.46 3.94h3.46V14zM4.5 2.5a.5.5 0 0 0-1 0v9.793l-1.146-1.147a.5.5 0 0 0-.708.708l2 1.999.007.007a.497.497 0 0 0 .7-.006l2-2a.5.5 0 0 0-.707-.708L4.5 12.293V2.5z"/></svg>') !important;
background-position: right 6px center !important;
background-repeat: no-repeat !important;
padding-right: 25px !important;
}
table.dataTable thead th.sorting_desc {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="%230d6efd" class="bi bi-sort-alpha-up-alt" viewBox="0 0 16 16"><path d="M12.96 7H8.5V5.701l3.46-3.94h-3.46V.51h4.46v1.298l-3.46 3.94h3.46V7zM4.5 13.5a.5.5 0 0 1-1 0V3.707L2.354 4.854a.5.5 0 1 1-.708-.708l2-1.999.007-.007a.498.498 0 0 1 .7.006l2 2a.5.5 0 1 1-.707.708L4.5 3.707V13.5zm5.582-7.871L9.664 7H8.598l1.789-5.332h1.234L13.402 7h-1.12l-.419-1.371h-1.781zm1.57-.785L11 2.687h-.047l-.652 2.157h1.351z"/></svg>') !important;
background-position: right 6px center !important;
background-repeat: no-repeat !important;
padding-right: 25px !important;
}
</style>
<div class="card p-4 shadow-sm mb-4">
<!-- 1. แถวหัวข้อและปุ่มเพิ่มรายการ -->
<div class="d-flex justify-content-between align-items-center mb-4 pb-2 border-bottom">
<h4 class="fw-bold mb-0"><i class="bi bi-list-task me-2 text-primary"></i>รายการจัดซื้อครุภัณฑ์ ประจำปี <?= $active_year ?></h4>
<div>
<button class="btn btn-success me-2 shadow-sm" onclick="alert('เปิดฟอร์มเพิ่มรายการใหม่')">
<i class="bi bi-plus-circle me-1"></i> เพิ่มรายการจัดซื้อ
</button>
<a href="index.php" class="btn btn-outline-secondary shadow-sm">
<i class="bi bi-arrow-left me-1"></i> กลับหน้าหลัก
</a>
</div>
</div>
<!-- 2. กล่องกรองข้อมูล (Filter Section) -->
<div class="row g-3 mb-4 bg-light p-3 rounded border shadow-sm">
<div class="col-md-3">
<label class="form-label fw-bold small text-muted">กรองตามหน่วยงาน</label>
<select id="deptFilter" class="form-select form-select-sm">
<option value="">-- ทั้งหมดทุกหน่วยงาน --</option>
<?php foreach ($departments as $dept): ?>
<option value="<?= htmlspecialchars($dept) ?>"><?= htmlspecialchars($dept) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<label class="form-label fw-bold small text-muted">กรองตามหมวดหมู่รายการ</label>
<select id="catFilter" class="form-select form-select-sm">
<option value="">-- ทั้งหมดทุกหมวดหมู่ --</option>
<?php foreach ($categories as $cat): ?>
<option value="<?= htmlspecialchars($cat['category']) ?>">
<?= $cat['category_code'] ? '[' . $cat['category_code'] . '] ' : '' ?><?= htmlspecialchars($cat['category']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3">
<label class="form-label fw-bold small text-muted">กรองตามสถานะการจัดซื้อ</label>
<select id="statusFilter" class="form-select form-select-sm">
<option value="">-- ทั้งหมดทุกสถานะ --</option>
<?php foreach ($statuses as $st): ?>
<option value="<?= htmlspecialchars($st) ?>"><?= htmlspecialchars($st) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-2 d-flex align-items-end">
<button class="btn btn-sm btn-secondary w-100" onclick="resetFilters()">
<i class="bi bi-arrow-counterclockwise me-1"></i> ล้างการกรอง
</button>
</div>
</div>
<!-- 3. Data Table -->
<div class="table-responsive">
<table id="procurementTable" class="table table-hover align-middle w-100 fs-6">
<thead class="table-light">
<tr>
<th class="text-nowrap text-center" style="width: 5%;">ลำดับ</th>
<th class="text-nowrap text-center" style="width: 8%;">รหัส</th>
<th class="text-nowrap text-center" style="width: 25%;">รายการครุภัณฑ์</th>
<th class="text-nowrap text-center">หมวดหมู่</th>
<th class="text-nowrap text-center">หน่วยงาน</th>
<th class="text-nowrap text-center">งบตั้งต้น</th>
<th class="text-nowrap text-center">จัดซื้อจริง</th>
<th class="text-nowrap text-center" style="width: 14%;">ความคืบหน้า</th>
<th class="text-nowrap text-center">สถานะปัจจุบัน</th>
<th class="text-nowrap text-center">Timeline</th>
<th class="text-nowrap text-center" style="width: 10%;">จัดการ</th>
</tr>
</thead>
<tbody>
<?php foreach ($items as $row): ?>
<tr>
<td class="text-center"><?= $row['seq_no'] ?></td>
<td class="text-center"><code><?= $row['item_code'] ?: '-' ?></code></td>
<td>
<div class="fw-bold text-dark"><?= htmlspecialchars($row['item_name']) ?></div>
<small class="text-muted"><?= $row['quantity'] ?> <?= $row['unit'] ?> (<?= $row['plan_type'] ?>)</small>
</td>
<td>
<span class="badge bg-light text-dark border" title="<?= htmlspecialchars($row['category']) ?>">
<?= $row['category_code'] ? '[' . $row['category_code'] . '] ' : '' ?><?= htmlspecialchars($row['category']) ?>
</span>
</td>
<td><?= htmlspecialchars($row['department']) ?></td>
<!-- งบตั้งต้น -->
<td class="text-end fw-bold"><?= number_format($row['total_price'], 0) ?></td>
<!-- จัดซื้อจริง -->
<td class="text-end text-success fw-bold">
<?= $row['procured_price'] > 0 ? number_format($row['procured_price'], 0) : '-' ?>
</td>
<!-- ความคืบหน้า -->
<td>
<?php
$p = (int)$row['progress'];
if ($p <= 30) {
$barClass = 'progress-bar-danger';
$badgeBg = 'bg-danger text-white';
} elseif ($p <= 70) {
$barClass = 'progress-bar-info';
$badgeBg = 'bg-primary text-white';
} else {
$barClass = 'progress-bar-success-custom';
$badgeBg = 'bg-success text-white';
}
?>
<div class="d-flex align-items-center">
<div class="custom-progress-container flex-grow-1 me-2">
<div class="progress-bar <?= $barClass ?> progress-bar-striped progress-bar-animated h-100" style="width: <?= $p ?>%"></div>
</div>
<span class="badge <?= $badgeBg ?> badge-progress shadow-sm"><?= $p ?>%</span>
</div>
</td>
<td class="text-center" data-sort="<?= $row['status'] ?>">
<?php
$badgeClass = match ($row['status']) {
'รอการยืนยันแผน' => 'bg-warning text-dark',
'อยู่ระหว่างดำเนินการ' => 'bg-primary text-white',
'ใบส่งซื้อ/ทำสัญญา' => 'bg-info text-dark',
'ดำเนินการเสร็จสิ้น' => 'bg-success text-white',
default => 'bg-secondary text-white'
};
?>
<select class="form-select form-select-sm text-center fw-bold border-0 shadow-sm <?= $badgeClass ?>"
style="border-radius: 20px; font-size: 0.85rem;"
data-id="<?= $row['id'] ?>"
onchange="changeStatus(this)">
<option value="รอการยืนยันแผน" <?= $row['status'] == 'รอการยืนยันแผน' ? 'selected' : '' ?>>รอการยืนยันแผน</option>
<option value="อยู่ระหว่างดำเนินการ" <?= $row['status'] == 'อยู่ระหว่างดำเนินการ' ? 'selected' : '' ?>>อยู่ระหว่างดำเนินการ</option>
<option value="ใบส่งซื้อ/ทำสัญญา" <?= $row['status'] == 'ใบส่งซื้อ/ทำสัญญา' ? 'selected' : '' ?>>ใบส่งซื้อ/ทำสัญญา</option>
<option value="ดำเนินการเสร็จสิ้น" <?= $row['status'] == 'ดำเนินการเสร็จสิ้น' ? 'selected' : '' ?>>ดำเนินการเสร็จสิ้น</option>
<option value="ยกเลิก" <?= $row['status'] == 'ยกเลิก' ? 'selected' : '' ?>>ยกเลิก</option>
</select>
</td>
<td class="text-center">
<?php if (!empty($row['confirm_period'])): ?>
<div class="small fw-bold text-muted mb-1"><i class="bi bi-calendar2-range me-1"></i><?= htmlspecialchars($row['confirm_period']) ?></div>
<?php endif; ?>
<?= getTimelineBadge($row['due_date'], $row['status']) ?>
</td>
<td class="text-center">
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-info" onclick="viewHistory(<?= $row['id'] ?>)" title="ดูประวัติและระยะเวลาดำเนินงาน">
<i class="bi bi-clock-history"></i>
</button>
<a href="qr_view.php?id=<?= $row['id'] ?>" target="_blank" class="btn btn-outline-dark" title="QR Code Mobile View">
<i class="bi bi-qr-code-scan"></i>
</a>
<?php if ($row['gdrive_folder_id']): ?>
<a href="https://drive.google.com/drive/folders/<?= $row['gdrive_folder_id'] ?>" target="_blank" class="btn btn-outline-primary" title="เปิดโฟลเดอร์ Google Drive">
<i class="bi bi-folder-symlink-fill"></i>
</a>
<?php endif; ?>
<button class="btn btn-outline-success" onclick="openApprovalModal(<?= $row['id'] ?>)" title="อนุมัติออนไลน์">
<i class="bi bi-check-circle"></i>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<!-- History Modal -->
<div class="modal fade" id="historyModal" tabindex="-1" aria-labelledby="historyModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header bg-info text-white">
<h5 class="modal-title" id="historyModalLabel"><i class="bi bi-clock-history me-2"></i>ประวัติการเปลี่ยนสถานะ</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="table-responsive">
<table class="table table-bordered table-striped" id="historyTable">
<thead class="table-light text-center">
<tr>
<th>วัน-เวลาที่เปลี่ยน</th>
<th>สถานะเดิม</th>
<th>สถานะใหม่</th>
<th>ระยะเวลาที่ใช้ (นับจากสถานะก่อนหน้า)</th>
</tr>
</thead>
<tbody id="historyBody">
<!-- Data will be loaded here -->
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">ปิด</button>
</div>
</div>
</div>
</div>
<!-- jQuery & DataTables JS -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
<script>
$(document).ready(function() {
var table = $('#procurementTable').DataTable({
"language": {
"url": "//cdn.datatables.net/plug-ins/1.13.6/i18n/th.json"
},
"pageLength": 10,
"order": [
[0, "asc"]
],
"columnDefs": [{
"orderable": false,
"targets": [7, 9, 10]
}]
});
// Custom Filters
$('#deptFilter').on('change', function() {
table.column(4).search(this.value).draw();
});
$('#catFilter').on('change', function() {
table.column(3).search(this.value).draw();
});
$('#statusFilter').on('change', function() {
table.column(8).search(this.value).draw();
});
});
function resetFilters() {
$('#deptFilter').val('');
$('#catFilter').val('');
$('#statusFilter').val('');
var table = $('#procurementTable').DataTable();
table.columns().search('').draw();
}
function viewHistory(id) {
$.ajax({
url: 'get_history.php',
type: 'GET',
data: { id: id },
dataType: 'json',
success: function(res) {
if (res.success) {
var tbody = $('#historyBody');
tbody.empty();
if (res.data.length === 0) {
tbody.append('<tr><td colspan="4" class="text-center text-muted">ไม่มีประวัติการเปลี่ยนสถานะ</td></tr>');
} else {
res.data.forEach(function(log) {
var row = `<tr>
<td class="text-center">${log.created_at}</td>
<td class="text-center"><span class="badge bg-secondary">${log.old_status || '-'}</span></td>
<td class="text-center"><span class="badge bg-primary">${log.new_status}</span></td>
<td class="text-center">${log.duration}</td>
</tr>`;
tbody.append(row);
});
}
var modal = new bootstrap.Modal(document.getElementById('historyModal'));
modal.show();
} else {
alert('ไม่สามารถดึงข้อมูลประวัติได้');
}
},
error: function() {
alert('เกิดข้อผิดพลาดในการเชื่อมต่อ');
}
});
}
function changeStatus(selectObj) {
var id = $(selectObj).data('id');
var newStatus = $(selectObj).val();
$(selectObj).prop('disabled', true);
$.ajax({
url: 'update_status.php',
type: 'POST',
data: { id: id, status: newStatus },
dataType: 'json',
success: function(res) {
if (res.success) {
// Refresh the page to update progress bars and badges correctly
location.reload();
} else {
alert('ไม่สามารถอัปเดตสถานะได้');
$(selectObj).prop('disabled', false);
}
},
error: function() {
alert('เกิดข้อผิดพลาดในการเชื่อมต่อระบบ');
$(selectObj).prop('disabled', false);
}
});
}
</script>
</body>
</html>