diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs index 4d3eedd9..4fdd66ca 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveBeginingRepository.cs @@ -88,7 +88,7 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests .Include(x => x.LeaveType) .FirstOrDefaultAsync(x => x.LeaveYear == year && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); - if(data == null) + if (data == null) { throw new Exception(GlobalMessages.DataNotFound); } @@ -165,5 +165,164 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests return data; } + public async Task GetByYearAndTypeIdForUser2Async(int year, Guid typeId, Guid userId) + { + var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(userId, AccessToken); + if (pf == null) + { + return null; + } + + var govAge = (pf?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + + var leaveType = await _dbContext.Set().FirstOrDefaultAsync(x => x.Id == typeId); + + var data = await _dbContext.Set() + .Include(x => x.LeaveType) + .FirstOrDefaultAsync(x => x.LeaveYear == year && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); + + if (data == null) + { + var limit = 0.0; + + var prev = await _dbContext.Set() + .Include(x => x.LeaveType) + .FirstOrDefaultAsync(x => x.LeaveYear == year - 1 && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); + + var prevRemain = 0.0; + if (prev != null) + { + prevRemain = prev.LeaveDays - prev.LeaveDaysUsed; + } + + if (govAge >= 180) + { + if (govAge >= 3650) + { + limit = 10 + prevRemain; + if (limit > 30) limit = 30; + } + else + { + limit = 10 + prevRemain; + if (limit > 20) limit = 20; + } + } + else + { + limit = 0.0; + } + + data = new LeaveBeginning + { + LeaveYear = year, + LeaveTypeId = typeId, + ProfileId = pf.Id, + Prefix = pf.Prefix, + FirstName = pf.FirstName, + LastName = pf.LastName, + LeaveDaysUsed = 0, + LeaveDays = leaveType?.Code == "LV-005" ? limit : 0 + }; + + _dbContext.Set().Add(data); + await _dbContext.SaveChangesAsync(); + } + + return data; + } + + public async Task> GetAllByYearAndTypeAsync(int year, Guid typeId, List userIdList) + { + var updateList = new List(); + var result = new List(); + + var beginningList = await _dbContext.Set() + .Include(x => x.LeaveType) + .Where(x => x.LeaveYear == year && x.LeaveTypeId == typeId) + .ToListAsync(); + + foreach (var pf in userIdList) + { + //var pf = await _userProfileRepository.GetProfileByKeycloakIdAsync(id, AccessToken); + //if (pf == null) + //{ + // continue; // Goto Next Id + //} + + var govAge = (pf?.DateStart?.Date ?? DateTime.Now.Date).DiffDay(DateTime.Now.Date); + + var leaveType = await _dbContext.Set().FirstOrDefaultAsync(x => x.Id == typeId); + + var data = beginningList.FirstOrDefault(x => x.ProfileId == pf.Id); + + if (data == null) + { + var limit = 0.0; + + var prev = await _dbContext.Set() + .Include(x => x.LeaveType) + .FirstOrDefaultAsync(x => x.LeaveYear == year - 1 && x.LeaveTypeId == typeId && x.ProfileId == pf.Id); + + var prevRemain = 0.0; + if (prev != null) + { + prevRemain = prev.LeaveDays - prev.LeaveDaysUsed; + } + + if (govAge >= 180) + { + if (govAge >= 3650) + { + limit = 10 + prevRemain; + if (limit > 30) limit = 30; + } + else + { + limit = 10 + prevRemain; + if (limit > 20) limit = 20; + } + } + else + { + limit = 0.0; + } + + data = new LeaveBeginning + { + LeaveYear = year, + LeaveTypeId = typeId, + ProfileId = pf.Id, + Prefix = pf.Prefix, + FirstName = pf.FirstName, + LastName = pf.LastName, + LeaveDaysUsed = 0, + LeaveDays = leaveType?.Code == "LV-005" ? limit : 0 + }; + + updateList.Add(data); + } + result.Add(data); + } + if (!updateList.Any()) + { + await _dbContext.Set().AddRangeAsync(updateList); + await _dbContext.SaveChangesAsync(); + } + return result; + } + } + + public class ProfileData + { + public Guid Id { get; set; } = Guid.Empty; + + public string Prefix { get; set; } = string.Empty; + + public string FirstName { get; set; } = string.Empty; + + public string LastName { get; set; } = string.Empty; + + public DateTime? DateStart { get; set; } = null; } } diff --git a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs index 6ef79b3a..ca34cbab 100644 --- a/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs +++ b/BMA.EHR.Application/Repositories/Leaves/LeaveRequests/LeaveRequestRepository.cs @@ -1454,7 +1454,8 @@ namespace BMA.EHR.Application.Repositories.Leaves.LeaveRequests KeycloakUserId = grp.Key.KeycloakUserId, LeaveTypeId = grp.Key.LeaveTypeId, LeaveTypeCode = grp.Key.LeaveTypeCode, - SumLeaveDay = grp.Sum(x => x.LeaveTotal) + SumLeaveDay = grp.Sum(x => x.LeaveTotal), + CountLeaveDay = grp.Count() }) .ToList(); diff --git a/BMA.EHR.Application/Responses/Leaves/GetSumApproveLeaveByTypeDto.cs b/BMA.EHR.Application/Responses/Leaves/GetSumApproveLeaveByTypeDto.cs index 5d73e0aa..2bc0079c 100644 --- a/BMA.EHR.Application/Responses/Leaves/GetSumApproveLeaveByTypeDto.cs +++ b/BMA.EHR.Application/Responses/Leaves/GetSumApproveLeaveByTypeDto.cs @@ -8,6 +8,8 @@ public string LeaveTypeCode { get; set; } = string.Empty; - public double SumLeaveDay { get; set; } + public double SumLeaveDay { get; set; } = 0.0; + + public int CountLeaveDay { get; set; } = 0; } } diff --git a/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdRootDto.cs b/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdRootDto.cs index 616295bb..c6d92ec5 100644 --- a/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdRootDto.cs +++ b/BMA.EHR.Application/Responses/Profiles/GetProfileByKeycloakIdRootDto.cs @@ -22,5 +22,9 @@ namespace BMA.EHR.Application.Responses.Profiles public string? OrgChild3Id { get; set; } public string? OrgChild4Id { get; set; } + public DateTime? DateStart { get; set; } + + public DateTime? DateAppoint { get; set; } + } } diff --git a/BMA.EHR.Insignia/BMA.EHR.Insignia.csproj b/BMA.EHR.Insignia/BMA.EHR.Insignia.csproj index 5903ceb7..275b4d20 100644 --- a/BMA.EHR.Insignia/BMA.EHR.Insignia.csproj +++ b/BMA.EHR.Insignia/BMA.EHR.Insignia.csproj @@ -1,4 +1,4 @@ - + net7.0 @@ -41,7 +41,7 @@ - + diff --git a/BMA.EHR.Insignia/Configuration/WebSocketConfiguration.cs b/BMA.EHR.Insignia/Configuration/WebSocketConfiguration.cs new file mode 100644 index 00000000..71ad08af --- /dev/null +++ b/BMA.EHR.Insignia/Configuration/WebSocketConfiguration.cs @@ -0,0 +1,16 @@ +namespace BMA.EHR.Insignia.Service.Configuration +{ + public class WebSocketConfiguration + { + public const string SectionName = "WebSocket"; + + public string Url { get; set; } = "https://bma-ehr.frappet.synology.me"; + public string Path { get; set; } = "/api/v1/org-socket"; + public string DefaultUserId { get; set; } = "4064c2b2-0414-464a-97c6-4a47c325b9a3"; + public int ReconnectionDelay { get; set; } = 1000; + public int ReconnectionAttempts { get; set; } = 5; + public int Timeout { get; set; } = 20000; + public bool AutoReconnect { get; set; } = true; + public int TaskDelayOnError { get; set; } = 5000; + } +} \ No newline at end of file diff --git a/BMA.EHR.Insignia/Services/InsigniaRequestProcessService.cs b/BMA.EHR.Insignia/Services/InsigniaRequestProcessService.cs index 2894a375..abaa5093 100644 --- a/BMA.EHR.Insignia/Services/InsigniaRequestProcessService.cs +++ b/BMA.EHR.Insignia/Services/InsigniaRequestProcessService.cs @@ -1,71 +1,63 @@ -using Microsoft.Extensions.Hosting; -using Quobject.SocketIoClientDotNet.Client; -using Sentry; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using System; +using BMA.EHR.Insignia.Service.Configuration; +using SocketIOClient; +using System.Security.Claims; + namespace BMA.EHR.Insignia.Service.Services; public class InsigniaRequestProcessService : BackgroundService { private readonly IBackgroundTaskQueue _queue; - private Socket _socket; - private bool _isConnected = false; - - public InsigniaRequestProcessService(IBackgroundTaskQueue queue) + private readonly IHttpContextAccessor _httpContextAccessor; + + + public InsigniaRequestProcessService( + IBackgroundTaskQueue queue, + IHttpContextAccessor httpContextAccessor) { _queue = queue; + _httpContextAccessor = httpContextAccessor; + + } - public override Task StartAsync(CancellationToken cancellationToken) + #region " Properties " + + protected string? UserId => _httpContextAccessor?.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; + + protected string? FullName => _httpContextAccessor?.HttpContext?.User?.FindFirst("name")?.Value; + + protected bool? IsPlacementAdmin => _httpContextAccessor?.HttpContext?.User?.IsInRole("placement1"); + + protected string? AccessToken => _httpContextAccessor?.HttpContext?.Request.Headers["Authorization"]; + + #endregion + + public override async Task StartAsync(CancellationToken cancellationToken) { - try + var client = new SocketIO("https://bma-ehr.frappet.synology.me/api/v1/org-socket", + new SocketIOOptions + { + // เพิ่ม token ใน handshake.auth + Auth = new { token = AccessToken ?? "" } + }); + + client.OnConnected += async (sender, e) => { - _socket = IO.Socket("https://bma-ehr.frappet.synology.me", new IO.Options - { - Path = "/api/v1/org-socket", - //Query = new Dictionary - //{ - // { "EIO", "4" }, - // { "transport", "polling" }, - // { "t", "tkitfptn" } - //} - }); + Console.WriteLine("Connected to Socket.IO server"); + await client.EmitAsync("eventName", "Hello from .NET client"); + }; - _socket.On(Socket.EVENT_CONNECT, () => - { - _isConnected = true; - Console.WriteLine("Connected to WebSocket server at: https://bma-ehr.frappet.synology.me/api/v1/org-socket"); - }); + await client.ConnectAsync(); - _socket.On(Socket.EVENT_DISCONNECT, () => - { - _isConnected = false; - Console.WriteLine("Disconnected from WebSocket server"); - }); - - _socket.On(Socket.EVENT_ERROR, (data) => - { - _isConnected = false; - Console.WriteLine($"WebSocket error: {data}"); - }); - - _socket.Connect(); - } - catch (Exception ex) - { - _isConnected = false; - Console.WriteLine($"Failed to initialize WebSocket connection: {ex.Message}"); - } - - return base.StartAsync(cancellationToken); + await base.StartAsync(cancellationToken); } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - var userId = "4064c2b2-0414-464a-97c6-4a47c325b9a3"; + while (!stoppingToken.IsCancellationRequested) { @@ -74,80 +66,34 @@ public class InsigniaRequestProcessService : BackgroundService var workItem = await _queue.DequeueAsync(stoppingToken); if (workItem != null) { - Console.WriteLine($"Starting background task at: {DateTime.Now}"); - await workItem(stoppingToken); - Console.WriteLine($"Finished background task at: {DateTime.Now}"); + var startTime = DateTime.Now; + + await workItem(stoppingToken); + + var endTime = DateTime.Now; + var duration = endTime - startTime; - // Send notification to WebSocket after task completion - if (_socket != null && _isConnected) - { - _socket.Emit("send-command-notification", - new - { - success = true, - message = "Background Task Completed Successfully", - payload = new { - completedAt = DateTime.Now, - taskType = "background_processing" - } - }, - new - { - userId = userId - }); - - Console.WriteLine("WebSocket notification sent successfully"); - } - else - { - Console.WriteLine("WebSocket is not connected. Unable to send notification."); - } } } + catch (OperationCanceledException) + { + + break; + } catch (Exception ex) { - Console.WriteLine($"Error processing background task: {ex.Message}"); - - // Send error notification to WebSocket - if (_socket != null && _isConnected) - { - _socket.Emit("send-command-notification", - new - { - success = false, - message = "Background Task Failed", - payload = new { - error = ex.Message, - failedAt = DateTime.Now, - taskType = "background_processing" - } - }, - new - { - userId = userId - }); - } + // รอสักครู่ก่อนประมวลผล task ถัดไป เพื่อป้องกันการวนลูปข้อผิดพลาด + await Task.Delay(1000, stoppingToken); } } + } - public override Task StopAsync(CancellationToken cancellationToken) + public override async Task StopAsync(CancellationToken cancellationToken) { - try - { - if (_socket != null) - { - Console.WriteLine("Disconnecting from WebSocket server..."); - _socket.Disconnect(); - _isConnected = false; - _socket = null; - } - } - catch (Exception ex) - { - Console.WriteLine($"Error disconnecting WebSocket: {ex.Message}"); - } - - return base.StopAsync(cancellationToken); + + + await base.StopAsync(cancellationToken); + } } diff --git a/BMA.EHR.Insignia/WebSocket-Improvements.md b/BMA.EHR.Insignia/WebSocket-Improvements.md new file mode 100644 index 00000000..cb6fc333 --- /dev/null +++ b/BMA.EHR.Insignia/WebSocket-Improvements.md @@ -0,0 +1,97 @@ +# WebSocket Connection Improvements for InsigniaRequestProcessService + +## ??????????????????? + +### 1. **????????? Configuration** +- ????? `WebSocketConfiguration` class ????????????????????? +- ?????????????????????? ???? appsettings.json +- ?? default values ??????????????????????? + +### 2. **??????????????????????? WebSocket** +- ??? `ImmutableList.Create()` ?????? Transports +- ????? event handlers ???????????????????????? +- ?????? reconnection ????????? +- Thread-safe ???? lock mechanism + +### 3. **??????????????? Logging** +- ??? `ILogger` ??? Console.WriteLine +- ????? emoji ??????????????? log ??????????? +- Log ?????????????????????????? + +### 4. **???????????????????** +- Proper exception handling ???????? +- Graceful handling ??? cancellation +- Delay ??????????????????????????????? busy loop + +### 5. **????????????????? Notification** +- ??????????????????????????????? +- ???????????????????????????????? +- ??????????? retry ????????? + +## ????????? + +### ???????????? appsettings.json +```json +{ + "WebSocket": { + "Url": "https://bma-ehr.frappet.synology.me", + "Path": "/api/v1/org-socket", + "DefaultUserId": "4064c2b2-0414-464a-97c6-4a47c325b9a3", + "ReconnectionDelay": 1000, + "ReconnectionAttempts": 5, + "Timeout": 20000, + "AutoReconnect": true, + "TaskDelayOnError": 5000 + } +} +``` + +### Log Messages ???????? +- ?? = Service started +- ?? = Task processing +- ? = Success operations +- ? = Error/Failure +- ?? = Reconnection attempts +- ?? = WebSocket notifications +- ?? = Service stopping + +## ??????????????????? + +1. **Reliability**: ???????????? WebSocket ??????????????????? +2. **Observability**: ???????????? log ??????? +3. **Configurability**: ??????????????????????? configuration +4. **Maintainability**: ???????????????????????? ?????????? +5. **Resilience**: ????????????????????????? + +## WebSocket Events ????????? + +- `EVENT_CONNECT`: ??????????????? +- `EVENT_DISCONNECT`: ???????????????? +- `EVENT_ERROR`: ???????????????? +- `EVENT_CONNECT_ERROR`: ???????????????????????? +- `EVENT_RECONNECT`: ??????????????????? +- `EVENT_RECONNECT_ERROR`: ???????????????????????????? +- `EVENT_RECONNECT_FAILED`: ????????????????????????? + +## Message Format ??????????? WebSocket + +```javascript +{ + "success": true/false, + "message": "Task Finish" ???? "Task Failed", + "payload": { + "completedAt": "timestamp", + "taskType": "insignia_background_processing", + "duration": 1234.56, // milliseconds + "status": "success" ???? "failed" + // ?????????? error ???? error ??? stackTrace ????? + } +} +``` + +???????? user data: +```javascript +{ + "userId": "4064c2b2-0414-464a-97c6-4a47c325b9a3" +} +``` \ No newline at end of file diff --git a/BMA.EHR.Insignia/appsettings.websocket.example.json b/BMA.EHR.Insignia/appsettings.websocket.example.json new file mode 100644 index 00000000..71f28243 --- /dev/null +++ b/BMA.EHR.Insignia/appsettings.websocket.example.json @@ -0,0 +1,12 @@ +{ + "WebSocket": { + "Url": "https://bma-ehr.frappet.synology.me", + "Path": "/api/v1/org-socket", + "DefaultUserId": "4064c2b2-0414-464a-97c6-4a47c325b9a3", + "ReconnectionDelay": 1000, + "ReconnectionAttempts": 5, + "Timeout": 20000, + "AutoReconnect": true, + "TaskDelayOnError": 5000 + } +} \ No newline at end of file diff --git a/BMA.EHR.Leave/BMA.EHR.Leave.csproj b/BMA.EHR.Leave/BMA.EHR.Leave.csproj index 84f1d58c..8d70a25d 100644 --- a/BMA.EHR.Leave/BMA.EHR.Leave.csproj +++ b/BMA.EHR.Leave/BMA.EHR.Leave.csproj @@ -53,6 +53,7 @@ + diff --git a/BMA.EHR.Leave/Controllers/LeaveReportController.cs b/BMA.EHR.Leave/Controllers/LeaveReportController.cs index 50949503..4cdec83e 100644 --- a/BMA.EHR.Leave/Controllers/LeaveReportController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveReportController.cs @@ -6,29 +6,17 @@ using BMA.EHR.Application.Repositories.MetaData; using BMA.EHR.Application.Responses.Profiles; using BMA.EHR.Domain.Common; using BMA.EHR.Domain.Extensions; -using BMA.EHR.Domain.Models.Leave.Commons; using BMA.EHR.Domain.Models.Leave.Requests; -using BMA.EHR.Domain.Models.MetaData; -using BMA.EHR.Domain.ModelsExam.Candidate; using BMA.EHR.Domain.Shared; using BMA.EHR.Leave.Service.DTOs.Reports; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.OpenApi.Any; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Org.BouncyCastle.Asn1.Pkcs; -using Org.BouncyCastle.Ocsp; -using Sentry; +using OfficeOpenXml; using Swashbuckle.AspNetCore.Annotations; -using System.Diagnostics.Eventing.Reader; -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")] @@ -1000,6 +988,8 @@ namespace BMA.EHR.Leave.Service.Controllers var leaveDays = await _leaveRequestRepository.GetSumApproveLeaveByTypeAndRange(req.StartDate, req.EndDate); var leaveTypes = await _leaveTypeRepository.GetAllAsync(); + + var count = 1; var employees = new List(); // กรองตามที่ fe ส่งมา @@ -1009,122 +999,327 @@ namespace BMA.EHR.Leave.Service.Controllers .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 reportType = req!.Type!.Trim().ToUpper(); + var year = req.EndDate.Year; + var profileList = profile.Select(x => new ProfileData + { + Id = x.Id, + Prefix = x.Prefix ?? "", + FirstName = x.FirstName ?? "", + LastName = x.LastName ?? "", + DateStart = x.DateStart ?? x.DateAppoint, + }).Distinct().ToList(); + + var beginningData = await _leaveBeginningRepository.GetAllByYearAsync(year); + + // sum all user + //var sickDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-001")?.Id ?? Guid.Empty, profileList!); + //var personalDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-002")?.Id ?? Guid.Empty, profileList!); + //var maternityDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-003")?.Id ?? Guid.Empty, profileList!); + //var wifeDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-004")?.Id ?? Guid.Empty, profileList!); + //var restDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-005")?.Id ?? Guid.Empty, profileList!); + //var ordainDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-006")?.Id ?? Guid.Empty, profileList!); + //var absentDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-007")?.Id ?? Guid.Empty, profileList!); + //var studyDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-008")?.Id ?? Guid.Empty, profileList!); + //var agencyDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-009")?.Id ?? Guid.Empty, profileList!); + //var coupleDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-010")?.Id ?? Guid.Empty, profileList!); + //var therapyDaySumALL = await _leaveBeginningRepository.GetAllByYearAndTypeAsync(year, leaveTypes.FirstOrDefault(x => x.Code == "LV-011")?.Id ?? Guid.Empty, profileList!); + foreach (var p in profile) { var keycloakUserId = p.Keycloak ?? Guid.Empty; - var sickDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-001"); - var sickDayCount = sickDay != null ? sickDay.SumLeaveDay : 0; - - var personalDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-002"); - var personalDayCount = personalDay != null ? personalDay.SumLeaveDay : 0; - - var maternityDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-003"); - var maternityDayCount = maternityDay != null ? maternityDay.SumLeaveDay : 0; - - var wifeDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-004"); - var wifeDayCount = wifeDay != null ? wifeDay.SumLeaveDay : 0; - - var restDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-005"); - var restDayCount = restDay != null ? restDay.SumLeaveDay : 0; - - var ordainDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-006"); - var ordainDayCount = ordainDay != null ? ordainDay.SumLeaveDay : 0; - - var absentDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-007"); - var absentDayCount = absentDay != null ? absentDay.SumLeaveDay : 0; - - var studyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-008"); - var studyDayCount = studyDay != null ? studyDay.SumLeaveDay : 0; - - var agencyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-009"); - var agencyDayCount = agencyDay != null ? agencyDay.SumLeaveDay : 0; - - var coupleDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-010"); - var coupleDayCount = coupleDay != null ? coupleDay.SumLeaveDay : 0; - - var therapyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-011"); - var therapyDayCount = therapyDay != null ? therapyDay.SumLeaveDay : 0; - - var timeStamps = await _processUserTimeStampRepository.GetTimeStampHistoryByRangeForUserAsync(p.Keycloak ?? Guid.Empty, req.StartDate, req.EndDate); - - var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); - if (defaultRound == null) + if (reportType == "FULL") { - return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); + + + var sickDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-001"); + var sickDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-001"); + var sickDayCount = sickDaySum != null ? sickDaySum.LeaveDaysUsed : 0; + var sickCount = sickDay != null ? sickDay.CountLeaveDay : 0; + + var personalDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-002"); + var personalDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-002"); + var personalDayCount = personalDaySum != null ? personalDaySum.LeaveDaysUsed : 0; + var personalCount = personalDay != null ? personalDay.CountLeaveDay : 0; + + var maternityDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-003"); + var maternityDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-003"); + var maternityDayCount = maternityDaySum != null ? maternityDaySum.LeaveDaysUsed : 0; + var maternityCount = maternityDay != null ? maternityDay.CountLeaveDay : 0; + + var wifeDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-004"); + var wifeDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-004"); + var wifeDayCount = wifeDaySum != null ? wifeDaySum.LeaveDaysUsed : 0; + var wifeCount = wifeDay != null ? wifeDay.CountLeaveDay : 0; + + var restDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-005"); + var restDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-005"); + var restDayCount = restDaySum != null ? restDaySum.LeaveDaysUsed : 0; + var restCount = restDay != null ? restDay.CountLeaveDay : 0; + + var ordainDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-006"); + var ordainDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-006"); + var ordainDayCount = ordainDaySum != null ? ordainDaySum.LeaveDaysUsed : 0; + var ordainCount = ordainDay != null ? ordainDay.CountLeaveDay : 0; + + var absentDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-007"); + var absentDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-007"); + var absentDayCount = absentDaySum != null ? absentDaySum.LeaveDaysUsed : 0; + var absentCount = absentDay != null ? absentDay.CountLeaveDay : 0; + + var studyDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-008"); + var studyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-008"); + var studyDayCount = studyDaySum != null ? studyDaySum.LeaveDaysUsed : 0; + var studyCount = studyDay != null ? studyDay.CountLeaveDay : 0; + + var agencyDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-009"); + var agencyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-009"); + var agencyDayCount = agencyDaySum != null ? agencyDaySum.LeaveDaysUsed : 0; + var agencyCount = agencyDay != null ? agencyDay.CountLeaveDay : 0; + + var coupleDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-010"); + var coupleDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-010"); + var coupleDayCount = coupleDaySum != null ? coupleDaySum.LeaveDaysUsed : 0; + var coupleCount = coupleDay != null ? coupleDay.CountLeaveDay : 0; + + var therapyDaySum = beginningData.FirstOrDefault(x => x.ProfileId == p.Id && x.LeaveType.Code == "LV-011"); + var therapyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-011"); + var therapyDayCount = therapyDaySum != null ? therapyDaySum.LeaveDaysUsed : 0; + var therapyCount = therapyDay != null ? therapyDay.CountLeaveDay : 0; + + var timeStamps = await _processUserTimeStampRepository.GetTimeStampHistoryByRangeForUserAsync(p.Keycloak ?? Guid.Empty, req.StartDate, req.EndDate); + + var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); + if (defaultRound == null) + { + return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); + } + //var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty); + 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; + + /* var processTimeStamps = timeStamps + .Select(d => new + { + d.Id, + CheckInStatus = DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > + DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? + "LATE" : + "NORMAL", + CheckOutStatus = d.CheckOut == null ? "" : + DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < + DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? + "LATE" : + DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < + DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? + "ABSENT" : + "NORMAL", + });*/ + + /*var absentCount = processTimeStamps.Count(x => x.CheckOutStatus == "ABSENT"); + var lateCount = processTimeStamps.Count(x => x.CheckInStatus == "LATE");*/ + + var absentCount1 = timeStamps.Count(d => + d.CheckOutStatus == "ABSENT" || d.CheckInStatus == "ABSENT"); // นับจำนวนที่มี CheckOutStatus == "ABSENT" + var lateCount1 = timeStamps.Count(d => + d.CheckInStatus == "LATE"); // นับจำนวนที่มี CheckInStatus == "LATE" + + + var emp = new + { + no = count, + fullName = $"{p.Prefix}{p.FirstName} {p.LastName}", + position = p.Position == null ? "" : p.Position, + positionLevel = p.PositionLevel == null ? "" : p.PositionLevel, + posNo = p.PosNo == null ? "" : p.PosNo, + reason = "", + sickDayCount = sickDayCount, + maternityDayCount = maternityDayCount, + wifeDayCount = wifeDayCount, + personalDayCount = personalDayCount, + restDayCount = restDayCount, + ordainDayCount = ordainDayCount, + absentDayCount = absentDayCount, + studyDayCount = studyDayCount, + agencyDayCount = agencyDayCount, + coupleDayCount = coupleDayCount, + therapyDayCount = therapyDayCount, + absentTotal = absentCount1, + lateTotal = lateCount1, + + sickCount = sickCount, + maternityCount = maternityCount, + wifeCount = wifeCount, + personalCount = personalCount, + restCount = restCount, + ordainCount = ordainCount, + absentCount = absentCount, + studyCount = studyCount, + agencyCount = agencyCount, + coupleCount = coupleCount, + therapyCount = therapyCount, + + leaveTotal = sickCount + + maternityCount + + wifeCount + + personalCount + + restCount + + ordainCount + + absentCount + + studyCount + + agencyCount + + coupleCount + + therapyCount + }; + + + employees.Add(emp); + count++; } - //var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty); - 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; - - /* var processTimeStamps = timeStamps - .Select(d => new - { - d.Id, - CheckInStatus = DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > - DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? - "LATE" : - "NORMAL", - CheckOutStatus = d.CheckOut == null ? "" : - DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < - DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? - "LATE" : - DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < - DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? - "ABSENT" : - "NORMAL", - });*/ - - /*var absentCount = processTimeStamps.Count(x => x.CheckOutStatus == "ABSENT"); - var lateCount = processTimeStamps.Count(x => x.CheckInStatus == "LATE");*/ - - var absentCount = timeStamps.Count(d => - d.CheckOutStatus == "ABSENT"); // นับจำนวนที่มี CheckOutStatus == "ABSENT" - var lateCount = timeStamps.Count(d => - d.CheckInStatus == "LATE"); // นับจำนวนที่มี CheckInStatus == "LATE" - - - var emp = new + else { - no = count, - fullName = $"{p.Prefix}{p.FirstName} {p.LastName}", - position = p.Position == null ? "" : p.Position, - positionLevel = p.PositionLevel == null ? "" : p.PositionLevel, - posNo = p.PosNo == null ? "" : p.PosNo, - reason = "", - sickDayCount = sickDayCount, - maternityDayCount = maternityDayCount, - wifeDayCount = wifeDayCount, - personalDayCount = personalDayCount, - restDayCount = restDayCount, - ordainDayCount = ordainDayCount, - absentDayCount = absentDayCount, - studyDayCount = studyDayCount, - agencyDayCount = agencyDayCount, - coupleDayCount = coupleDayCount, - therapyDayCount = therapyDayCount, - absentTotal = absentCount, - lateTotal = lateCount, - - leaveTotal = sickDayCount + - maternityDayCount + - wifeDayCount + - personalDayCount + - restDayCount + - ordainDayCount + - absentDayCount + - studyDayCount + - agencyDayCount + - coupleDayCount + - therapyDayCount - }; + var sickDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-001"); + var sickDayCount = sickDay != null ? sickDay.SumLeaveDay : 0; + var sickCount = sickDay != null ? sickDay.CountLeaveDay : 0; - employees.Add(emp); - count++; + var personalDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-002"); + var personalDayCount = personalDay != null ? personalDay.SumLeaveDay : 0; + var personalCount = personalDay != null ? personalDay.CountLeaveDay : 0; + + var maternityDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-003"); + var maternityDayCount = maternityDay != null ? maternityDay.SumLeaveDay : 0; + var maternityCount = maternityDay != null ? maternityDay.CountLeaveDay : 0; + + var wifeDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-004"); + var wifeDayCount = wifeDay != null ? wifeDay.SumLeaveDay : 0; + var wifeCount = wifeDay != null ? wifeDay.CountLeaveDay : 0; + + var restDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-005"); + var restDayCount = restDay != null ? restDay.SumLeaveDay : 0; + var restCount = restDay != null ? restDay.CountLeaveDay : 0; + + var ordainDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-006"); + var ordainDayCount = ordainDay != null ? ordainDay.SumLeaveDay : 0; + var ordainCount = ordainDay != null ? ordainDay.CountLeaveDay : 0; + + var absentDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-007"); + var absentDayCount = absentDay != null ? absentDay.SumLeaveDay : 0; + var absentCount = absentDay != null ? absentDay.CountLeaveDay : 0; + + var studyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-008"); + var studyDayCount = studyDay != null ? studyDay.SumLeaveDay : 0; + var studyCount = studyDay != null ? studyDay.CountLeaveDay : 0; + + var agencyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-009"); + var agencyDayCount = agencyDay != null ? agencyDay.SumLeaveDay : 0; + var agencyCount = agencyDay != null ? agencyDay.CountLeaveDay : 0; + + var coupleDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-010"); + var coupleDayCount = coupleDay != null ? coupleDay.SumLeaveDay : 0; + var coupleCount = coupleDay != null ? coupleDay.CountLeaveDay : 0; + + var therapyDay = leaveDays.FirstOrDefault(x => x.KeycloakUserId == keycloakUserId && x.LeaveTypeCode == "LV-011"); + var therapyDayCount = therapyDay != null ? therapyDay.SumLeaveDay : 0; + var therapyCount = therapyDay != null ? therapyDay.CountLeaveDay : 0; + + var timeStamps = await _processUserTimeStampRepository.GetTimeStampHistoryByRangeForUserAsync(p.Keycloak ?? Guid.Empty, req.StartDate, req.EndDate); + + var defaultRound = await _dutyTimeRepository.GetDefaultAsync(); + if (defaultRound == null) + { + return Error("ไม่พบรอบการลงเวลา Default", StatusCodes.Status404NotFound); + } + //var userRound = await _dutyTimeRepository.GetByIdAsync(profile.DutyTimeId ?? Guid.Empty); + 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; + + /* var processTimeStamps = timeStamps + .Select(d => new + { + d.Id, + CheckInStatus = DateTime.Parse(d.CheckIn.ToString("yyyy-MM-dd HH:mm")) > + DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.StartTimeMorning}") ? + "LATE" : + "NORMAL", + CheckOutStatus = d.CheckOut == null ? "" : + DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < + DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeAfternoon}") ? + "LATE" : + DateTime.Parse(d.CheckOut.Value.ToString("yyyy-MM-dd HH:mm")) < + DateTime.Parse($"{d.CheckIn.Date.ToString("yyyy-MM-dd")} {duty.EndTimeMorning}") ? + "ABSENT" : + "NORMAL", + });*/ + + /*var absentCount = processTimeStamps.Count(x => x.CheckOutStatus == "ABSENT"); + var lateCount = processTimeStamps.Count(x => x.CheckInStatus == "LATE");*/ + + var absentCount2 = timeStamps.Count(d => + d.CheckOutStatus == "ABSENT" || d.CheckInStatus == "ABSENT"); // นับจำนวนที่มี CheckOutStatus == "ABSENT" + var lateCount2 = timeStamps.Count(d => + d.CheckInStatus == "LATE"); // นับจำนวนที่มี CheckInStatus == "LATE" + + + var emp = new + { + no = count, + fullName = $"{p.Prefix}{p.FirstName} {p.LastName}", + position = p.Position == null ? "" : p.Position, + positionLevel = p.PositionLevel == null ? "" : p.PositionLevel, + posNo = p.PosNo == null ? "" : p.PosNo, + reason = "", + sickDayCount = sickDayCount, + maternityDayCount = maternityDayCount, + wifeDayCount = wifeDayCount, + personalDayCount = personalDayCount, + restDayCount = restDayCount, + ordainDayCount = ordainDayCount, + absentDayCount = absentDayCount, + studyDayCount = studyDayCount, + agencyDayCount = agencyDayCount, + coupleDayCount = coupleDayCount, + therapyDayCount = therapyDayCount, + absentTotal = absentCount2, + lateTotal = lateCount2, + + sickCount = sickCount, + maternityCount = maternityCount, + wifeCount = wifeCount, + personalCount = personalCount, + restCount = restCount, + ordainCount = ordainCount, + absentCount = absentCount, + studyCount = studyCount, + agencyCount = agencyCount, + coupleCount = coupleCount, + therapyCount = therapyCount, + + leaveTotal = sickCount + + maternityCount + + wifeCount + + personalCount + + restCount + + ordainCount + + absentCount + + studyCount + + agencyCount + + coupleCount + + therapyCount + }; + + + employees.Add(emp); + count++; + } } var leaveTitleType = ""; @@ -1983,7 +2178,7 @@ namespace BMA.EHR.Leave.Service.Controllers worksheet.Cells[2, 1].Value = organizationName; worksheet.Cells[3, 1].Value = dateTimeStamp; - using (var range = worksheet.Cells[4, 1 ,4, 10]) + using (var range = worksheet.Cells[4, 1, 4, 10]) { range.Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; range.Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; @@ -2020,7 +2215,7 @@ namespace BMA.EHR.Leave.Service.Controllers } // ใส่กรอบให้ตาราง - using (var range = worksheet.Cells[5, 1, startRow-1, 10]) + using (var range = worksheet.Cells[5, 1, startRow - 1, 10]) { range.Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; range.Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin; diff --git a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs index 226f25e3..1c31a597 100644 --- a/BMA.EHR.Leave/Controllers/LeaveRequestController.cs +++ b/BMA.EHR.Leave/Controllers/LeaveRequestController.cs @@ -2246,7 +2246,7 @@ namespace BMA.EHR.Leave.Service.Controllers //approver = list.First().Name; //} - var leaveSummary = await _leaveRequestRepository.GetSumApproveLeaveByTypeForUserAsync(rawData.KeycloakUserId, rawData.Type.Id, thisYear); + //var sumLeave = rawData.LeaveStartDate.DiffDay(rawData.LeaveEndDate); //var sumHoliday = await _holidayRepository.GetHolidayCountAsync(rawData.LeaveStartDate, rawData.LeaveEndDate, category); @@ -2267,7 +2267,8 @@ namespace BMA.EHR.Leave.Service.Controllers if (rawData.Root != null && rawData.Root != "") orgName += $" {rawData.Root}"; - var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUserAsync(thisYear, rawData.Type.Id, rawData.KeycloakUserId); + var leaveData = await _leaveBeginningRepository.GetByYearAndTypeIdForUser2Async(thisYear, rawData.Type.Id, rawData.KeycloakUserId); + var leaveSummary = leaveData == null ? 0.0 : leaveData.LeaveDaysUsed; var extendLeave = 0.0; var leaveLimit = (double)rawData.Type.Limit;