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 00000000..3c84bb60 Binary files /dev/null and b/BMA.EHR.Leave/Reports/TimeStampRecords.xlsx differ