413 lines
22 KiB
PHP
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>
|