From ef9fafecfcaa62b089a3ca5ee7b2ccb00e3b6abb Mon Sep 17 00:00:00 2001 From: Bright Date: Thu, 21 Aug 2025 17:33:19 +0700 Subject: [PATCH] =?UTF-8?q?timeStamp=20report=20=E0=B9=81=E0=B8=9A?= =?UTF-8?q?=E0=B8=9A=20generate=20=E0=B8=95=E0=B8=A3=E0=B8=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/LeaveReportController.cs | 357 +++++++++++++++++- BMA.EHR.Leave/Reports/TimeStampRecords.xlsx | Bin 0 -> 18324 bytes 2 files changed, 355 insertions(+), 2 deletions(-) create mode 100644 BMA.EHR.Leave/Reports/TimeStampRecords.xlsx diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 7f190f3b..ab028941 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -26,7 +26,9 @@ using System.Diagnostics.Metrics; using System.Globalization; using System.Security.Claims; using static Nest.JoinField; - +using Microsoft.AspNetCore.Mvc; +using OfficeOpenXml; +using Microsoft.AspNetCore.Routing.Template; namespace BMA.EHR.Leave.Service.Controllers { [Route("api/v{version:apiVersion}/leave/report")] @@ -52,6 +54,7 @@ namespace BMA.EHR.Leave.Service.Controllers private readonly IHttpContextAccessor _httpContextAccessor; private readonly PermissionRepository _permission; private readonly LeaveBeginningRepository _leaveBeginningRepository; + private readonly IWebHostEnvironment _hostingEnvironment; #endregion @@ -68,7 +71,8 @@ namespace BMA.EHR.Leave.Service.Controllers UserCalendarRepository userCalendarRepository, IHttpContextAccessor httpContextAccessor, PermissionRepository permission, - LeaveBeginningRepository leaveBeginningRepository) + LeaveBeginningRepository leaveBeginningRepository, + IWebHostEnvironment hostingEnvironment) { _leaveRequestRepository = leaveRequestRepository; _userProfileRepository = userProfileRepository; @@ -82,6 +86,7 @@ namespace BMA.EHR.Leave.Service.Controllers _httpContextAccessor = httpContextAccessor; _permission = permission; _leaveBeginningRepository = leaveBeginningRepository; + _hostingEnvironment = hostingEnvironment; } private class LoopDate { @@ -1678,6 +1683,354 @@ namespace BMA.EHR.Leave.Service.Controllers } } + /// + /// LV2_037 - รายงานการเข้างานจำแนกรายวัน รายสัปดาห์ รายเดือน แยกรายหน่วยงาน/ส่วนราชการ + /// + /// + /// + /// เมื่อทำรายการสำเร็จ + /// ไม่ได้ Login เข้าระบบ + /// เมื่อเกิดข้อผิดพลาดในการทำงาน + [HttpPost("download/time-records/{type}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> GetTimeRecordsNewOfficerReport([FromBody] GetLeaveDetailByNodeReportDto req, string type) + { + try + { + var getPermission = await _permission.GetPermissionAPIAsync("LIST", "SYS_LEAVE_REPORT"); + var jsonData = JsonConvert.DeserializeObject(getPermission); + if (jsonData["status"]?.ToString() != "200") + { + return Error(jsonData["message"]?.ToString(), StatusCodes.Status403Forbidden); + } + var profile = new List(); + string role = jsonData["result"]?.ToString(); + var nodeId = string.Empty; + var profileAdmin = new GetUserOCAllDto(); + profileAdmin = await _userProfileRepository.GetUserOCAll(Guid.Parse(UserId!), AccessToken); + if (role == "NORMAL" || role == "CHILD") + { + nodeId = profileAdmin?.Node == 4 + ? profileAdmin?.Child4DnaId + : profileAdmin?.Node == 3 + ? profileAdmin?.Child3DnaId + : profileAdmin?.Node == 2 + ? profileAdmin?.Child2DnaId + : profileAdmin?.Node == 1 + ? profileAdmin?.Child1DnaId + : profileAdmin?.Node == 0 + ? profileAdmin?.RootDnaId + : ""; + } + else if (role == "ROOT") + { + nodeId = profileAdmin?.RootDnaId; + } + if (type.Trim().ToUpper() == "OFFICER") + { + profile = await _userProfileRepository.GetProfileByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId); + } + else + { + profile = await _userProfileRepository.GetEmployeeByAdminRole(AccessToken, profileAdmin?.Node, nodeId, role, req.revisionId); + } + var date = req.StartDate.Date; + + var holidays = await _holidayRepository.GetHolidayAsync(req.StartDate.Date, req.EndDate.Date); + var weekend = _holidayRepository.GetWeekEnd(req.StartDate.Date, req.EndDate.Date); + var excludeDates = holidays.Union(weekend).ToList(); + + var dateList = new List(); + for (DateTime i = req.StartDate.Date; i <= req.EndDate.Date; i = i.AddDays(1)) + { + + if (holidays.Contains(i)) + { + var d = await _holidayRepository.GetHolidayAsync(i); + dateList.Add(new LoopDate + { + date = i, + isHoliday = true, + isWeekEnd = false, + dateRemark = d + }); + } + else if (weekend.Contains(i)) + { + dateList.Add(new LoopDate + { + date = i, + isHoliday = true, + isWeekEnd = false, + dateRemark = "วันหยุด" + }); + } + else + { + dateList.Add(new LoopDate + { + date = i, + isHoliday = false, + isWeekEnd = false, + dateRemark = "" + }); + } + } + + var employees = new List(); + var count = 1; + + var restTotal = 0; + var sickTotal = 0; + var lateTotal = 0; + var wfhTotal = 0; + var studyTotal = 0; + var workTotal = 0; + var seminarTotal = 0; + // กรองตามที่ fe ส่งมา + if (role == "ROOT" || role == "OWNER" || role == "CHILD") + { + profile = profile + .Where(x => req.node == 4 ? x.OrgChild4Id == req.nodeId : req.node == 3 ? x.OrgChild3Id == req.nodeId : req.node == 2 ? x.OrgChild2Id == req.nodeId : req.node == 1 ? x.OrgChild1Id == req.nodeId : req.node == 0 ? x.OrgRootId == req.nodeId : true) + .ToList(); + } + var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); + if (defaultRound == null) + { + return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); + } + foreach (var dd in dateList) + { + foreach (var p in profile) + { + var keycloakUserId = p.Keycloak ?? Guid.Empty; + + var timeStamps = await _processUserTimeStampRepository.GetTimestampByDateAsync(keycloakUserId, dd.date); + + var fullName = $"{p.Prefix}{p.FirstName} {p.LastName}"; + + var effectiveDate = await _userDutyTimeRepository.GetLastEffectRound(p.Id); + var roundId = effectiveDate != null ? effectiveDate.DutyTimeId : Guid.Empty; + var userRound = await _dutyTimeRepository.GetByIdAsync(roundId); + + var duty = userRound ?? defaultRound; + + // check วันลาของแต่ละคน + var leaveReq = await _leaveRequestRepository.GetLeavePeriodAsync(keycloakUserId, dd.date); + var remarkStr = string.Empty; + + if (leaveReq != null) + { + switch (leaveReq.Type.Code.ToUpper()) + { + case "LV-001": + case "LV-002": + case "LV-005": + remarkStr += leaveReq.Type.Name; + var leaveRange = leaveReq.LeaveRange == null ? "" : leaveReq.LeaveRange.ToUpper(); + if (leaveRange == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRange == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; + + + var leaveRangeEnd = leaveReq.LeaveRangeEnd == null ? "" : leaveReq.LeaveRangeEnd.ToUpper(); + if (leaveRangeEnd == "MORNING") + remarkStr += "ครึ่งวันเช้า"; + else if (leaveRangeEnd == "AFTERNOON") + remarkStr += "ครึ่งวันบ่าย"; + break; + default: + remarkStr += leaveReq.Type.Name; + break; + } + } + else + { + if (timeStamps == null) + { + if (dd.date <= DateTime.Now.Date) + { + remarkStr = "ขาดราชการ"; + if (dd.isHoliday == true) + { + remarkStr = $"วันหยุด ({dd.dateRemark})"; + } + else if (dd.isWeekEnd) + { + remarkStr = dd.dateRemark; + } + } + else remarkStr = ""; + } + else + { + // check status ของการลงเวลา + if (timeStamps.CheckOut != null) + { + if (timeStamps.CheckOutStatus == "ABSENT") + remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckOut ? $" (นอกสถานที่:{timeStamps.CheckOutLocationName})".Trim() : ""); + else if (timeStamps.CheckInStatus == "ABSENT") + remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + else if (timeStamps.CheckInStatus == "LATE") + { + remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + lateTotal += 1; + } + else + remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : ""; + } + else + { + if (timeStamps.CheckInStatus == "ABSENT") + remarkStr = "ขาดราชการ" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + else if (timeStamps.CheckInStatus == "LATE") + { + remarkStr = "สาย" + (!timeStamps.IsLocationCheckIn ? $" (นอกสถานที่:{timeStamps.CheckInLocationName})".Trim() : ""); + lateTotal += 1; + } + else + remarkStr = !timeStamps.IsLocationCheckIn ? $" นอกสถานที่:{timeStamps.CheckInLocationName}".Trim() : ""; + } + } + } + + var emp = new DateResultReport + { + no = count, + fullName = fullName, + dutyTimeName = $"{duty.StartTimeMorning} - {duty.EndTimeAfternoon} น.", + checkInLocation = timeStamps == null ? "" : timeStamps.CheckInPOI, + checkInTime = timeStamps == null ? "" : $"{timeStamps.CheckIn.ToString("HH:mm")} น.", + checkOutLocation = timeStamps == null ? "" : timeStamps.CheckOutPOI ?? "", + checkOutTime = timeStamps == null ? "" : + timeStamps.CheckOut != null ? + $"{timeStamps.CheckOut.Value.ToString("HH:mm")} น." : + "", + remark = remarkStr, + checkInDate = timeStamps == null ? dd.date.Date.ToThaiFullDate2() : timeStamps.CheckIn.Date.ToThaiFullDate2(), + checkedOutDate = timeStamps == null ? dd.date.Date.ToThaiFullDate2() : + timeStamps.CheckOut != null ? + timeStamps.CheckOut.Value.ToThaiFullDate2() : + "", + checkInTimeRaw = timeStamps?.CheckIn, + checkOutTimeRaw = timeStamps?.CheckOut, + }; + + if (timeStamps != null) + { + workTotal += 1; + if (!timeStamps.IsLocationCheckIn) + { + if (timeStamps.CheckInLocationName == "ปฏิบัติงานที่บ้าน") + wfhTotal += 1; + else if (timeStamps.CheckInLocationName == "ไปประชุม/อบรม/สัมมนา/ปฏิบัติงานที่บ้านนอกสถานที่") + seminarTotal += 1; + } + } + + if (leaveReq != null) + { + switch (leaveReq.Type.Code.ToUpper()) + { + case "LV-001": + case "LV-002": + sickTotal += 1; + break; + case "LV-005": + restTotal += 1; + break; + case "LV-008": + studyTotal += 1; + break; + } + } + + employees.Add(emp); + count++; + } + } + employees = employees.OrderBy(x => x.checkInDate).ThenBy(x => x.checkInTimeRaw ?? DateTime.MaxValue).ThenBy(x => x.checkOutTimeRaw ?? DateTime.MaxValue).ToList(); + for (int i = 0; i < employees.Count; i++) + { + employees[i].no = i + 1; + } + + var enddate = req.EndDate.Date == req.StartDate.Date ? "" : $" - {req.EndDate.Date.ToThaiShortDate()}"; + var org = _userProfileRepository.GetOc(Guid.Parse(req.nodeId), req.node, AccessToken); + var organizationName = $"{(!string.IsNullOrEmpty(org.Child4) ? org.Child4 + "/" : "")}{(!string.IsNullOrEmpty(org.Child3) ? org.Child3 + "/" : "")}{(!string.IsNullOrEmpty(org.Child2) ? org.Child2 + "/" : "")}{(!string.IsNullOrEmpty(org.Child1) ? org.Child1 + "/" : "")}{org.Root ?? ""}"; + var dateTimeStamp = $"ประจำ ณ วัน{req.StartDate.Date.GetThaiDayOfWeek()} ที่ {req.StartDate.Date.ToThaiShortDate()}{enddate}"; + var template = System.IO.Path.Combine(_hostingEnvironment.ContentRootPath, "Reports", "TimeStampRecords.xlsx"); + FileInfo reportFile = new FileInfo(template); + using (var package = new ExcelPackage(reportFile)) + { + var worksheet = package.Workbook.Worksheets[0]; + worksheet.Cells[2, 1].Value = organizationName; + worksheet.Cells[3, 1].Value = dateTimeStamp; + int startRow = 5; + foreach (var emp in employees) + { + worksheet.Cells[startRow, 1].Value = emp.no; + worksheet.Cells[startRow, 2].Value = emp.fullName; + worksheet.Cells[startRow, 3].Value = emp.dutyTimeName; + worksheet.Cells[startRow, 4].Value = emp.checkInDate; + worksheet.Cells[startRow, 5].Value = emp.checkInLocation; + worksheet.Cells[startRow, 6].Value = emp.checkInTime; + worksheet.Cells[startRow, 7].Value = emp.checkedOutDate; + worksheet.Cells[startRow, 8].Value = emp.checkOutLocation; + worksheet.Cells[startRow, 9].Value = emp.checkOutTime; + worksheet.Cells[startRow, 10].Value = emp.remark; + // ใส่กรอบให้ตาราง + using (var range = worksheet.Cells[startRow, 1, startRow, 10]) + { + range.Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + range.Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + range.Style.Border.Left.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + range.Style.Border.Right.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; + } + startRow++; + } + int lastRow = startRow + 2; + worksheet.Cells[lastRow, 2].Value = type.Trim().ToUpper() == "OFFICER" ? "ข้าราชการทั้งหมด" : "ลูกจ้างประจำทั้งหมด"; + worksheet.Cells[lastRow, 5].Value = profile?.Count; + worksheet.Cells[lastRow, 6].Value = "คน"; + worksheet.Cells[lastRow, 8].Value = "นอกสถานที่ตั้ง"; + worksheet.Cells[lastRow + 1, 2].Value = "มาปฏิบัติราชการ"; + worksheet.Cells[lastRow + 1, 5].Value = workTotal; + worksheet.Cells[lastRow + 1, 6].Value = "คน"; + worksheet.Cells[lastRow + 1, 8].Value = "WFH"; + worksheet.Cells[lastRow + 1, 9].Value = wfhTotal; + worksheet.Cells[lastRow + 1, 10].Value = "คน"; + worksheet.Cells[lastRow + 2, 2].Value = "ลาพักผ่อน"; + worksheet.Cells[lastRow + 2, 5].Value = restTotal; + worksheet.Cells[lastRow + 2, 6].Value = "คน"; + worksheet.Cells[lastRow + 2, 8].Value = "อบรม ประชุม สัมมนาฯ"; + worksheet.Cells[lastRow + 2, 9].Value = seminarTotal; + worksheet.Cells[lastRow + 2, 10].Value = "คน"; + worksheet.Cells[lastRow + 3, 2].Value = "ลาป่วย/ลากิจ"; + worksheet.Cells[lastRow + 3, 5].Value = sickTotal; + worksheet.Cells[lastRow + 3, 6].Value = "คน"; + worksheet.Cells[lastRow + 4, 2].Value = "มาสาย"; + worksheet.Cells[lastRow + 4, 5].Value = lateTotal; + worksheet.Cells[lastRow + 4, 6].Value = "คน"; + worksheet.Cells[lastRow + 6, 2].Value = "เรียน"; + worksheet.Cells[lastRow + 7, 2].Value = "เพื่อโปรดทราบ"; + worksheet.Cells[lastRow + 7, 9].Value = "ทราบ"; + worksheet.Cells[lastRow + 8, 2].Value = "................................"; + worksheet.Cells[lastRow + 8, 9].Value = "................................"; + worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns(); + var fileBytes = package.GetAsByteArray(); + return File(fileBytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "TimeStampRecords.xlsx"); + } + } + catch (Exception ex) + { + return Error(ex); + } + } + /// /// LV2_035 - รายงานการเข้างานสาย จำแนกเป็นรายบุคคลและหรือรายหน่วยงาน/ส่วนราชการ /// diff --git a/BMA.EHR.Leave/Reports/TimeStampRecords.xlsx b/BMA.EHR.Leave/Reports/TimeStampRecords.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..3c84bb60d2348c1347748d269d00956847bbb1de GIT binary patch literal 18324 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